Skip to content

Commit

Permalink
(#26) Make MIT-SHM support available via -d:mitshm build flag
Browse files Browse the repository at this point in the history
  • Loading branch information
rexim committed Dec 12, 2019
1 parent cc5d79e commit eb57fe8
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 97 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ See issue [#26]. For an experimental Live Update feature compile the application
$ nimble build -d:live
```

For a faster Live Update feature based on MIT-SHM X11 extension use `-d:mitshm`:

```console
$ nimble build -d:live -d:mitshm
```

The feature is really unstable and experimental, so use it at your own risk.

## NixOS Overlay
Expand Down
88 changes: 23 additions & 65 deletions src/boomer.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os

import navigation
import image
import screenshot
import config

import x11/xlib, x11/x, x11/xutil, x11/keysym, x11/xrandr, x11/xshm
Expand All @@ -11,11 +11,6 @@ import syscall

type Shader = tuple[path, content: string]

const
IPC_PRIVATE = 0
IPC_CREAT = 512
IPC_RMID = 0

proc readShader(file: string): Shader =
when nimvm:
result.path = file
Expand Down Expand Up @@ -97,7 +92,7 @@ proc update(flashlight: var Flashlight, dt: float32) =
else:
flashlight.shadow = max(flashlight.shadow - 6.0 * dt, 0.0)

proc draw(screenshot: Image, camera: Camera, shader, vao, texture: GLuint,
proc draw(screenshot: PXImage, camera: Camera, shader, vao, texture: GLuint,
windowSize: Vec2f, mouse: Mouse, flashlight: Flashlight) =
glClearColor(0.1, 0.1, 0.1, 1.0)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
Expand Down Expand Up @@ -181,27 +176,6 @@ proc main() =
DefaultRootWindow(display),
addr attributes)

var shminfo: TXShmSegmentInfo
var screenshot = XShmCreateImage(
display, vi.visual, 24.cuint, ZPixmap, nil,
addr shminfo,
attributes.width.cuint,
attributes.height.cuint)

shminfo.shmid = syscall(SHMGET,
IPC_PRIVATE,
screenshot.bytes_per_line * screenshot.height,
IPC_CREAT or 0o777).cint
shminfo.shmaddr = cast[cstring](syscall(SHMAT, shminfo.shmid, 0, 0))
screenshot.data = shminfo.shmaddr
shminfo.readOnly = 0

let err = XShmAttach(display, addr shminfo)
echo "Status of XShmAttach call = ", err
discard XSync(display, 0)

discard XShmGetImage(display, DefaultRootWindow(display), screenshot, 0.cint, 0.cint, AllPlanes);

var win = XCreateWindow(
display, root,
0, 0, attributes.width.cuint, attributes.height.cuint, 0,
Expand Down Expand Up @@ -231,8 +205,15 @@ proc main() =

var shaderProgram = newShaderProgram(vertexShader, fragmentShader)

let w = screenshot.width.float32
let h = screenshot.height.float32
var screenshot =
when defined(mitshm):
newMitshmScreenshot(display)
else:
newDefaultScreenshot(display)
defer: screenshot.destroy(display)

let w = screenshot.image.width.float32
let h = screenshot.image.height.float32
var
vao, vbo, ebo: GLuint
vertices = [
Expand Down Expand Up @@ -279,13 +260,13 @@ proc main() =
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB.GLint,
screenshot.width,
screenshot.height,
screenshot.image.width,
screenshot.image.height,
0,
# TODO(#13): the texture format is hardcoded
GL_BGRA,
GL_UNSIGNED_BYTE,
screenshot.data)
screenshot.image.data)
glGenerateMipmap(GL_TEXTURE_2D)

glUniform1i(glGetUniformLocation(shaderProgram, "tex".cstring), 0)
Expand Down Expand Up @@ -403,52 +384,29 @@ proc main() =
else:
discard

camera.update(config, dt, mouse, screenshot,
camera.update(config, dt, mouse, screenshot.image,
vec2(wa.width.float32, wa.height.float32))
flashlight.update(dt)

screenshot.draw(camera, shaderProgram, vao, texture,
vec2(wa.width.float32, wa.height.float32),
mouse, flashlight)
screenshot.image.draw(camera, shaderProgram, vao, texture,
vec2(wa.width.float32, wa.height.float32),
mouse, flashlight)

glXSwapBuffers(display, win)
glFinish()

discard XShmGetImage(display, DefaultRootWindow(display), screenshot, 0.cint, 0.cint, AllPlanes);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB.GLint,
screenshot.width,
screenshot.height,
0,
# TODO(#13): the texture format is hardcoded
GL_BGRA,
GL_UNSIGNED_BYTE,
screenshot.data)

when defined(live):
screenshot = XGetSubImage(display, root,
0, 0,
screenshot.width.cuint,
screenshot.height.cuint,
AllPlanes,
ZPixmap,
screenshot,
0, 0)
screenshot.refresh(display)
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB.GLint,
screenshot.width,
screenshot.height,
screenshot.image.width,
screenshot.image.height,
0,
# TODO(#13): the texture format is hardcoded
GL_BGRA,
GL_UNSIGNED_BYTE,
screenshot.data)

discard XShmDetach(display, addr shminfo)
discard XDestroyImage(screenshot)
discard syscall(SHMDT, shminfo.shmaddr)
discard syscall(SHMCTL, shminfo.shmid, IPC_RMID, 0)
screenshot.image.data)
discard XSync(display, 0)

main()
29 changes: 0 additions & 29 deletions src/image.nim

This file was deleted.

6 changes: 3 additions & 3 deletions src/navigation.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import x11/xlib

import config
import la
import image

const VELOCITY_THRESHOLD = 15.0

Expand All @@ -19,8 +20,7 @@ type Camera* = object
proc world*(camera: Camera, v: Vec2f): Vec2f =
v / camera.scale

proc update*(camera: var Camera, config: Config, dt: float, mouse: Mouse, image: Image,
windowSize: Vec2f) =
proc update*(camera: var Camera, config: Config, dt: float, mouse: Mouse, image: PXImage, windowSize: Vec2f) =
if abs(camera.deltaScale) > 0.5:
let p0 = (camera.scalePivot - (windowSize * 0.5)) / camera.scale
camera.scale = max(camera.scale + camera.delta_scale * dt, 0.01)
Expand Down
108 changes: 108 additions & 0 deletions src/screenshot.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import x11/xlib, x11/x, x11/xutil, x11/xshm
import syscall

const
IPC_PRIVATE = 0
IPC_CREAT = 512
IPC_RMID = 0

type ScreenshotBackend* = enum
DEFAULT, # XGetImage
MITSHM # XShmGetImage

type Screenshot* = object
backend*: ScreenshotBackend
image*: PXImage
shminfo*: PXShmSegmentInfo

proc newDefaultScreenshot*(display: PDisplay): Screenshot =
result.backend = DEFAULT

var root = DefaultRootWindow(display)
var attributes: TXWindowAttributes
discard XGetWindowAttributes(display, root, addr attributes)
result.image = XGetImage(display, root,
0, 0,
attributes.width.cuint,
attributes.height.cuint,
AllPlanes,
ZPixmap)

proc newMitshmScreenshot*(display: PDisplay): Screenshot =
result.backend = MITSHM
result.shminfo = cast[PXShmSegmentInfo](allocShared(sizeof(TXShmSegmentInfo)))

var root = DefaultRootWindow(display)
var attributes: TXWindowAttributes
discard XGetWindowAttributes(display, root, addr attributes)

let screen = DefaultScreen(display)
result.image = XShmCreateImage(
display,
DefaultVisual(display, screen),
DefaultDepthOfScreen(ScreenOfDisplay(display, screen)).cuint,
ZPixmap,
nil,
result.shminfo,
attributes.width.cuint,
attributes.height.cuint)

result.shminfo.shmid = syscall(
SHMGET,
IPC_PRIVATE,
result.image.bytes_per_line * result.image.height,
IPC_CREAT or 0o777).cint

result.shminfo.shmaddr = cast[cstring](syscall(
SHMAT,
result.shminfo.shmid,
0, 0))
result.image.data = result.shminfo.shmaddr
result.shminfo.readOnly = 0

discard XShmAttach(display, result.shminfo)
discard XShmGetImage(display, root, result.image, 0.cint, 0.cint, AllPlanes)

proc refresh*(screenshot: var Screenshot, display: PDisplay) =
var root = DefaultRootWindow(display)

case screenshot.backend
of DEFAULT:
screenshot.image =
XGetSubImage(display, root,
0, 0,
screenshot.image.width.cuint,
screenshot.image.height.cuint,
AllPlanes,
ZPixmap,
screenshot.image,
0, 0)
of MITSHM:
discard XShmGetImage(
display,
root, screenshot.image,
0.cint, 0.cint,
AllPlanes)

proc destroy*(screenshot: var Screenshot, display: PDisplay) =
case screenshot.backend
of DEFAULT:
discard XDestroyImage(screenshot.image)
of MITSHM:
discard XSync(display, 0)
discard XShmDetach(display, screenshot.shminfo)
discard XDestroyImage(screenshot.image)
discard syscall(SHMDT, screenshot.shminfo.shmaddr)
discard syscall(SHMCTL, screenshot.shminfo.shmid, IPC_RMID, 0)
deallocShared(screenshot.shminfo)

proc saveToPPM*(image: PXImage, filePath: string) =
var f = open(filePath, fmWrite)
defer: f.close
writeLine(f, "P6")
writeLine(f, image.width, " ", image.height)
writeLine(f, 255)
for i in 0..<(image.width * image.height):
f.write(image.data[i * 4 + 2])
f.write(image.data[i * 4 + 1])
f.write(image.data[i * 4 + 0])

0 comments on commit eb57fe8

Please sign in to comment.