(#26) Make MIT-SHM support available via -d:mitshm build flag

This commit is contained in:
rexim 2019-12-12 21:46:20 +07:00
parent cc5d79ebbe
commit eb57fe89ee
5 changed files with 140 additions and 97 deletions

View File

@ -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

View File

@ -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
@ -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
@ -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)
@ -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,
@ -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 = [
@ -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)
@ -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()

View File

@ -1,29 +0,0 @@
import x11/xlib, x11/x
type Image* = PXImage
proc saveToPPM*(image: Image, 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])
# NOTE: it's not possible to deallocate the returned Image because the
# reference to XImage is lost.
proc takeScreenshot*(display: PDisplay, root: TWindow): Image =
var attributes: TXWindowAttributes
discard XGetWindowAttributes(display, root, addr attributes)
result = XGetImage(display, root,
0, 0,
attributes.width.cuint,
attributes.height.cuint,
AllPlanes,
ZPixmap)
if result == nil:
quit "Could not get a screenshot"

View File

@ -1,6 +1,7 @@
import x11/xlib
import config
import la
import image
const VELOCITY_THRESHOLD = 15.0
@ -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)

108
src/screenshot.nim Normal file
View File

@ -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])