From 68c4a6ed5259c43224965addfc4bb76d1b98bad5 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Thu, 28 Oct 2021 11:33:34 +0300 Subject: [PATCH 01/14] wayland re-use freed ids --- src/windy/platforms/linux/wayland/basic.nim | 85 +++++++++---------- .../platforms/linux/wayland/protocol.nim | 7 +- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/windy/platforms/linux/wayland/basic.nim b/src/windy/platforms/linux/wayland/basic.nim index ef28226..875b078 100644 --- a/src/windy/platforms/linux/wayland/basic.nim +++ b/src/windy/platforms/linux/wayland/basic.nim @@ -15,10 +15,6 @@ type socket: Socket ids: Table[uint32, Proxy] - lastId: Id - - Callback* = ref object of Proxy - done: proc(cbData: uint32) proc `//>`[T: SomeInteger](a, b: T): T = @@ -44,12 +40,48 @@ proc asString[T](x: openarray[T]): string = cast[string](x.asSeq(char)) +proc connect*(name = getEnv("WAYLAND_SOCKET")): Display = + new result, (proc(d: Display) = close d.socket) + + result.display = result + result.id = Id 1 + result.ids[1] = result + + let d = result + result.deleteId = proc(id: Id) = + d.ids.del id.uint32 + + var name = + if name != "": $name + else: "wayland-0" + + if not name.isAbsolute: + var runtimeDir = getEnv("XDG_RUNTIME_DIR") + if runtimeDir == "": raise WindyError.newException("XDG_RUNTIME_DIR not set in the environment") + name = runtimeDir / name + + let sock = createNativeSocket(posix.AF_UNIX, posix.SOCK_STREAM or posix.SOCK_CLOEXEC, 0) + if sock == osInvalidSocket: raise WindyError.newException("Failed to create socket") + + var a = "\1\0" & name + + if sock.connect(cast[ptr SockAddr](a[0].addr), uint32 name.len + 2) < 0: + close sock + raise WindyError.newException("Failed to connect to wayland server") + + result.socket = newSocket(sock, nativesockets.AF_UNIX, nativesockets.SOCK_STREAM, nativesockets.IPPROTO_IP) + + proc new(d: Display, t: type): t = - inc d.lastId + proc findHole: uint32 = + for k in 2 ..< (2 + d.ids.len.uint32): + if not d.ids.hasKey k: return k + + let id = findHole() new result result.display = d - result.id = d.lastId - d.ids[d.lastId.uint32] = result + result.id = Id id + d.ids[id] = result proc destroy*(x: Proxy) = x.display.ids.del x.id.uint32 @@ -170,42 +202,3 @@ proc pollNextEvent(d: Display) = if not d.ids.hasKey id: return # event for destroyed object d.ids[id].unmarshal(op.int, data) - - -proc connect*(name = getEnv("WAYLAND_SOCKET")): Display = - new result, (proc(d: Display) = close d.socket) - - result.display = result - result.id = Id 1 - result.lastId = Id 1 - result.ids[1] = result - - let d = result - result.deleteId = proc(id: Id) = - if id.uint32 == 2: return # re-use Callback reserved for syncing - d.ids.del id.uint32 - - var name = - if name != "": $name - else: "wayland-0" - - if not name.isAbsolute: - var runtimeDir = getEnv("XDG_RUNTIME_DIR") - if runtimeDir == "": raise WindyError.newException("XDG_RUNTIME_DIR not set in the environment") - name = runtimeDir / name - - let sock = createNativeSocket(posix.AF_UNIX, posix.SOCK_STREAM or posix.SOCK_CLOEXEC, 0) - if sock == osInvalidSocket: raise WindyError.newException("Failed to create socket") - - var a = "\1\0" & name - - if sock.connect(cast[ptr SockAddr](a[0].addr), uint32 name.len + 2) < 0: - close sock - raise WindyError.newException("Failed to connect to wayland server") - - result.socket = newSocket(sock, nativesockets.AF_UNIX, nativesockets.SOCK_STREAM, nativesockets.IPPROTO_IP) - - # reserve Callback for syncing - discard result.new(Callback) - result.marshal(0, Id 2) - result.pollNextEvent diff --git a/src/windy/platforms/linux/wayland/protocol.nim b/src/windy/platforms/linux/wayland/protocol.nim index c546dd0..98a2e91 100644 --- a/src/windy/platforms/linux/wayland/protocol.nim +++ b/src/windy/platforms/linux/wayland/protocol.nim @@ -155,7 +155,7 @@ macro protocol(body) = `unms` result.insert 0, nnkTypeSection.newTree( # type declaration - types.filterit($it[0] notin ["Display", "Callback"]).mapit( # do not redefine Display and Callback + types.filterit($it[0] != "Display").mapit( # do not redefine Display nnkTypeDef.newTree( nnkPostfix.newTree(ident"*", it[0]), newEmptyNode(), @@ -511,10 +511,9 @@ protocol: proc sync*(this: Display) = + let cb = this.syncRequest var done: bool - this.ids[2].Callback.onDone: - done = true - this.marshal(0, Id 2) + cb.onDone: done = true while not done: this.pollNextEvent From c40afb1d55cad66824274de7953517ff765850bb Mon Sep 17 00:00:00 2001 From: levovix0 Date: Thu, 28 Oct 2021 11:52:01 +0300 Subject: [PATCH 02/14] option dnd action --- src/windy/platforms/linux/wayland.nim | 2 +- src/windy/platforms/linux/wayland/protocol.nim | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index 79b5878..5b6d263 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -8,7 +8,7 @@ let reg = display.registry var compositor: Compositor reg.onGlobal: - echo (id: name.uint32, iface: iface, version: version) + echo (name: name, iface: iface, version: version) case iface of "wl_compositor": compositor = reg.bindInterface(Compositor, name, iface, version) diff --git a/src/windy/platforms/linux/wayland/protocol.nim b/src/windy/platforms/linux/wayland/protocol.nim index 98a2e91..3977058 100644 --- a/src/windy/platforms/linux/wayland/protocol.nim +++ b/src/windy/platforms/linux/wayland/protocol.nim @@ -1,5 +1,5 @@ include basic -import macros, strformat, sequtils, vmath +import macros, strformat, sequtils, vmath, options proc unshl(x: int): int = var x = x @@ -7,8 +7,13 @@ proc unshl(x: int): int = inc result x = x shr 1 -proc toBitfield(x: enum): int = 1 shl x.int -proc fromBitfield(x: int, T: type): T = x.unshl.T +proc toBitfield(x: Option[enum]): int = + if x.isNone: 0 + else: 1 shl x.get.int +proc fromBitfield(x: int, t: type[Option]): t = + type T = t.T + if x == 0: none T + else: some x.unshl.T macro protocol(body) = proc injectAllParams(x: NimNode): NimNode = @@ -247,7 +252,6 @@ type uyvy = 0x59565955 DndAction* {.pure.} = enum - none copy move ask @@ -358,11 +362,11 @@ protocol: proc receive(mime: string, fd: FileDescriptor) proc destroy proc finish - proc setActions(actions: set[DndAction.copy..DndAction.ask], prefered: bitField DndAction) + proc setActions(actions: set[DndAction], prefered: bitField Option[DndAction]) proc offer(mime: string): event proc sourceActions(actions: set[DndAction]): event - proc action(action: bitField DndAction): event + proc action(action: bitField Option[DndAction]): event DataSource: @@ -375,7 +379,7 @@ protocol: proc cancelled: event proc dndDropPerformed: event proc dndFinished: event - proc action(action: bitField DndAction): event + proc action(action: bitField Option[DndAction]): event DataDevice: From 60767f3b1a798fb8fa4862c8b1083f026979fc98 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Thu, 28 Oct 2021 13:11:19 +0300 Subject: [PATCH 03/14] replace deptrcated wl_shell via xdg_wm_base --- .../platforms/linux/wayland/protocol.nim | 153 +++++++++++++----- 1 file changed, 116 insertions(+), 37 deletions(-) diff --git a/src/windy/platforms/linux/wayland/protocol.nim b/src/windy/platforms/linux/wayland/protocol.nim index 3977058..485cd61 100644 --- a/src/windy/platforms/linux/wayland/protocol.nim +++ b/src/windy/platforms/linux/wayland/protocol.nim @@ -54,6 +54,9 @@ macro protocol(body) = var i = 0 for a in x[1]: + if a.kind == nnkCommand and a[0] == ident"iface": + continue #TODO: auto iface require + let p = a.params if p[0] == ident"event": @@ -256,21 +259,6 @@ type move ask - Edge* {.pure.} = enum - top - bottom - left - right - - TransientFlag* {.pure.} = enum - inactive - - FullscreenMethod* {.pure.} = enum - default - scale - driver - fill - Transform* {.pure.} = enum normal rotated90 @@ -311,6 +299,41 @@ type ModeFlag* {.pure.} = enum current prefered + + Anchor* {.pure.} = enum + none + top + bottom + left + right + topLeft + bottomLeft + topRight + bottomRight + + ConstraintAllignment* {.pure.} = enum + slideX + slideY + flipX + flipY + resizeX + resizeY + + Edge* {.pure.} = enum + top + bottom + left + right + + ShellSurfaceState* {.pure.} = enum + maximized = 1 + fullscreen + resizing + activated + tiledLeft + tiledRight + tiledTop + tiledBottom @@ -335,6 +358,8 @@ protocol: Compositor: + iface "wl_compositor" + proc newSurface: Surface proc newRegion: Region @@ -346,6 +371,8 @@ protocol: Shm: + iface "wl_shm" + proc newPool(fd: FileDescriptor, size: int): ShmPool proc format(format: ShmFormat): event @@ -396,31 +423,12 @@ protocol: DataDeviceManager: + iface "wl_data_device_manager" + proc newDataSource: DataSource proc dataDevice(seat: Seat): DataDevice - Shell: - proc shellSurface(surface: Surface): ShellSurface - - - ShellSurface: - proc pong(serial: int) - proc move(seat: Seat, serial: int) - proc resize(seat: Seat, serial: int, edges: set[Edge]) - proc setToplevel - proc setTransient(parent: Surface, pos: IVec2, flags: set[TransientFlag]) - proc setFullscreen(m: FullscreenMethod, framerate: int, output: Output) - proc setPopup(seat: Seat, serial: int, parent: Surface, pos: IVec2, flags: set[TransientFlag]) - proc setMaximized(output: Output) - proc setTitle(title: string) - proc setClass(class: string) - - proc ping(serial: int): event - proc configure(edges: set[Edge], size: IVec2): event - proc popupDone: event - - Surface: proc destroy proc attach(buffer: Buffer, pos: IVec2) @@ -438,6 +446,8 @@ protocol: Seat: + iface "wl_seat" + proc cursor: Cursor proc keyboard: Keyboard proc touch: Touch @@ -486,6 +496,8 @@ protocol: Output: + iface "wl_output" + proc destroy proc geometry(pos: IVec2, sizeInMillimeters: IVec2, subpixel: Subpixel, make: string, model: string, transform: Transform): event @@ -501,17 +513,84 @@ protocol: Subcompositor: + iface "wl_subcompositor" + proc destroy proc subsurface(surface: Surface, parent: Surface): Subsurface Subsurface: proc destroy - proc `pos=`(pos: IVec2) + proc `pos=`(v: IVec2) proc placeAbove(sibling: Surface) proc placeBelow(sibling: Surface) proc setSync proc setDesync + + + XdgWmBase: + iface "xdg_wm_base" + + proc destroy + proc newPositioner: Positioner + proc shellSurface(surface: Surface): ShellSurface + proc pong(serial: int) + + proc ping(serial: int): event + + + Positioner: + proc destroy + proc `size=`(v: IVec2) + proc setAnchorRect(pos: IVec2, size: IVec2) + proc `anchor=`(v: Anchor) + proc `gravity=`(v: int) + proc `constraintAllignment=`(v: set[ConstraintAllignment]) + proc `offset=`(v: IVec2) + proc setRelative + proc `parentSize=`(v: IVec2) + proc setParentConfigure(serial: int) + + + ShellSurface: + proc destroy + proc toplevel: Toplevel + proc popup(parent: ShellSurface, positioner: Positioner): Popup + proc setGeometry(pos: IVec2, size: IVec2) + proc ackConfigure(serial: int) + + proc configure(serial: int): event + + + Toplevel: + proc destroy + proc `parent=`(v: Toplevel) + proc `title=`(v: string) + proc `appId=`(v: string) + proc showWindowMenu(seat: Seat, serial: int, pos: IVec2) + proc move(seat: Seat, serial: int) + proc resize(seat: Seat, serial: int, edges: set[Edge]) + proc `maxSize=`(v: IVec2) + proc `minSize=`(v: IVec2) + proc maximize + proc unmaximize + proc fullscreen + proc unfullscreen + proc minimize + + proc configure(size: IVec2, states: seq[ShellSurfaceState]): event + proc close: event + + + Popup: + proc destroy + proc grub(seat: Seat, serial: int) + proc reposition(positioner: Positioner, token: int) + + proc configure(pos: IVec2, size: IVec2): event + proc done: event + proc repositioned(token: int): event + proc sync*(this: Display) = From 449d04ff2be2bdf0a3eff946196f31efdacdd1b8 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Thu, 16 Dec 2021 23:15:58 +0300 Subject: [PATCH 04/14] initializing on wayland --- src/windy/platforms/linux/wayland.nim | 61 +++++++++++++++---- src/windy/platforms/linux/wayland/basic.nim | 4 +- .../platforms/linux/wayland/protocol.nim | 9 ++- src/windy/platforms/linux/x11.nim | 8 +-- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index 5b6d263..e35a44e 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -1,16 +1,55 @@ +import ../../common, ../../internal import wayland/protocol -let display = connect() -display.onError: - echo "Error for ", objId.uint32, ": ", code, ", ", message +var + initialized: bool -let reg = display.registry -var compositor: Compositor + display: Display + registry: Registry -reg.onGlobal: - echo (name: name, iface: iface, version: version) - case iface - of "wl_compositor": - compositor = reg.bindInterface(Compositor, name, iface, version) + compositor: Compositor + shm: Shm + shell: XdgWmBase -sync display + shmFormats: seq[ShmFormat] + + +proc init* = + if initialized: return + + display = connect() + display.onError: + raise WindyError.newException("Wayland error for " & $objId.uint32 & ": " & $code & ", " & message) + + registry = display.registry + + registry.onGlobal: + case iface + of "wl_compositor": + compositor = registry.bindInterface(Compositor, name, iface, version) + + of "wl_shm": + shm = registry.bindInterface(Shm, name, iface, version) + + shm.onFormat: + shmFormats.add format + + of "xdg_wm_base": + shell = registry.bindInterface(XdgWmBase, name, iface, version) + + shell.onPing: + shell.pong(serial) + + sync display + + if compositor == nil or shm == nil or shell == nil: + raise WindyError.newException( + "Not enough Wayland interfaces, missing: " & + (if compositor == nil: "wl_compositor " else: "") & + (if shm == nil: "wl_shm " else: "") & + (if shell == nil: "xdg_wm_base " else: "") + ) + + sync display + + initialized = true diff --git a/src/windy/platforms/linux/wayland/basic.nim b/src/windy/platforms/linux/wayland/basic.nim index 875b078..d6d56e1 100644 --- a/src/windy/platforms/linux/wayland/basic.nim +++ b/src/windy/platforms/linux/wayland/basic.nim @@ -65,7 +65,7 @@ proc connect*(name = getEnv("WAYLAND_SOCKET")): Display = var a = "\1\0" & name - if sock.connect(cast[ptr SockAddr](a[0].addr), uint32 name.len + 2) < 0: + if sock.connect(cast[ptr SockAddr](a[0].addr), uint32 a.len) < 0: close sock raise WindyError.newException("Failed to connect to wayland server") @@ -119,7 +119,7 @@ proc serialize[T](x: T): seq[uint32] = result.add x.serialize elif x is set: - when T.sizeof > uint.sizeof: {.error: "too large set".} + when T.sizeof > uint32.sizeof: {.error: "too large set".} result.add cast[uint32](x) elif x is Proxy: diff --git a/src/windy/platforms/linux/wayland/protocol.nim b/src/windy/platforms/linux/wayland/protocol.nim index 485cd61..0dd091a 100644 --- a/src/windy/platforms/linux/wayland/protocol.nim +++ b/src/windy/platforms/linux/wayland/protocol.nim @@ -1,5 +1,5 @@ include basic -import macros, strformat, sequtils, vmath, options +import macros, strformat, sequtils, vmath, options, unicode proc unshl(x: int): int = var x = x @@ -70,7 +70,7 @@ macro protocol(body) = newEmptyNode() ) result.add nnkTemplateDef.newTree( # x.onEvent:... template - nnkPostfix.newTree(ident"*", ident &"on{a.name}"), + nnkPostfix.newTree(ident"*", ident &"on{($a.name).runeAt(0).toUpper}{($a.name).toRunes[1..^1]}"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( @@ -126,8 +126,7 @@ macro protocol(body) = else: # marshaling let (_, _, argvals) = p.prepareArgs p.insert 1, newIdentDefs(ident"this", t) - let name = a.name - a.name = nnkPostfix.newTree(ident"*", a.name) + a[0] = nnkPostfix.newTree(ident"*", a[0]) a.params = p.prepareParams a.body = nnkCall.newTree( @[nnkDotExpr.newTree(ident"this", ident"marshal"), newLit i] & @@ -147,7 +146,7 @@ macro protocol(body) = ), a.body ) - if $name == "destroy": + if $a.name == "destroy": a.body = newStmtList( a.body, newCall(ident"destroy", nnkDotExpr.newTree(ident"this", ident"Proxy")) diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index e8123ce..f230748 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -46,8 +46,6 @@ type motiv kwm other - - ButtonView* = distinct set[Button] var @@ -145,10 +143,10 @@ proc invert[T](x: var set[T], v: T) = if x.contains v: x.excl v else: x.incl v -proc send*(a: XWindow, e: XEvent, mask: clong = NoEventMask, propagate = false) = +proc send(a: XWindow, e: XEvent, mask: clong = NoEventMask, propagate = false) = display.XSendEvent(a, propagate, mask, e.unsafeAddr) -proc newClientMessage*[T](window: XWindow, messageKind: Atom, data: openarray[T], serial: int = 0, sendEvent: bool = false): XEvent = +proc newClientMessage[T](window: XWindow, messageKind: Atom, data: openarray[T], serial: int = 0, sendEvent: bool = false): XEvent = result.kind = xeClientMessage result.client.messageType = messageKind if data.len * T.sizeof > XClientMessageData.sizeof: @@ -284,7 +282,7 @@ proc keysymToButton(sym: KeySym): Button = of xk_9: Key9 else: ButtonUnknown -proc queryKeyboardState*(): set[0..255] = +proc queryKeyboardState(): set[0..255] = var r: array[32, char] display.XQueryKeymap(r) result = cast[ptr set[0..255]](r.addr)[] From 02ac67895b0666709af75c3aed3e25c2de0fa0df Mon Sep 17 00:00:00 2001 From: levovix0 Date: Thu, 16 Dec 2021 23:53:20 +0300 Subject: [PATCH 05/14] fix basic example crush when x11 and -d:danger --- src/windy/platforms/linux/x11.nim | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index f230748..464a60c 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -456,15 +456,13 @@ proc newWindow*( visible = true, vsync = true, - # window constructor can't set opengl version, msaa and stencilBits - # args mustn't set depthBits directly openglMajorVersion = 4, openglMinorVersion = 1, msaa = msaaDisabled, depthBits = 24, stencilBits = 8, - transparent = false, # note that transparency CANNOT be changed after window was created + transparent = false, ): Window = ## Creates a new window. Intitializes Windy if needed. init() @@ -473,13 +471,15 @@ proc newWindow*( result.runeInputEnabled = true let root = display.defaultRootWindow - + var vi: XVisualInfo if transparent: display.XMatchVisualInfo(display.defaultScreen, 32, TrueColor, vi.addr) else: - var attribList = [GlxRgba, GlxDepthSize, 24, GlxDoublebuffer] - vi = display.glXChooseVisual(display.defaultScreen, attribList[0].addr)[] + display.XMatchVisualInfo(display.defaultScreen, 24, TrueColor, vi.addr) + # strangely, glx rerutns nil when -d:danger + # var attribList = [GlxRgba, GlxDepthSize, 24, GlxDoublebuffer] + # vi = display.glXChooseVisual(display.defaultScreen, attribList[0].addr)[] let cmap = display.XCreateColormap(root, vi.visual, AllocNone) var swa = XSetWindowAttributes(colormap: cmap) @@ -521,7 +521,7 @@ proc newWindow*( result.ctx = display.glXCreateContext(vi.addr, nil, 1) if result.ctx == nil: - raise newException(WindyError, "Error creating OpenGL context") + raise WindyError.newException("Error creating OpenGL context") result.title = title @@ -535,7 +535,7 @@ proc newWindow*( elif glXSwapIntervalSGI != nil: glXSwapIntervalSGI(1) else: - raise newException(WindyError, "VSync is not supported") + raise WindyError.newException("VSync is not supported") if visible: result.visible = true @@ -708,9 +708,6 @@ proc buttonReleased*(window: Window): ButtonView = proc buttonToggle*(window: Window): ButtonView = ButtonView window.buttonToggle -proc `[]`*(buttonView: ButtonView, button: Button): bool = - button in (set[Button])(buttonView) - proc closeRequested*(window: Window): bool = window.closeRequested From de83584cdc9f36f51fa22abd96022dd5aa002254 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Thu, 16 Dec 2021 23:53:39 +0300 Subject: [PATCH 06/14] rm x11_2 --- src/windy/platforms/linux/x11_2.nim | 164 ---------------------------- 1 file changed, 164 deletions(-) delete mode 100644 src/windy/platforms/linux/x11_2.nim diff --git a/src/windy/platforms/linux/x11_2.nim b/src/windy/platforms/linux/x11_2.nim deleted file mode 100644 index 9ceade6..0000000 --- a/src/windy/platforms/linux/x11_2.nim +++ /dev/null @@ -1,164 +0,0 @@ -import ../../common, vmath - -type - Window* = ref object - onCloseRequest*: Callback - onMove*: Callback - onResize*: Callback - onFocusChange*: Callback - onMouseMove*: Callback - onScroll*: Callback - onButtonPress*: ButtonCallback - onButtonRelease*: ButtonCallback - onRune*: RuneCallback - onImeChange*: Callback - -proc title*(window: Window): string = - discard - -proc closed*(window: Window): bool = - discard - -proc visible*(window: Window): bool = - discard - -proc decorated*(window: Window): bool = - discard - -proc resizable*(window: Window): bool = - discard - -proc fullscreen*(window: Window): bool = - discard - -proc size*(window: Window): IVec2 = - discard - -proc pos*(window: Window): IVec2 = - discard - -proc minimized*(window: Window): bool = - discard - -proc maximized*(window: Window): bool = - discard - -proc framebufferSize*(window: Window): IVec2 = - discard - -proc contentScale*(window: Window): float32 = - discard - -proc focused*(window: Window): bool = - discard - -proc mousePos*(window: Window): IVec2 = - discard - -proc mousePrevPos*(window: Window): IVec2 = - discard - -proc mouseDelta*(window: Window): IVec2 = - discard - -proc scrollDelta*(window: Window): Vec2 = - discard - -proc closeRequested*(window: Window): bool = - discard - -proc imeCursorIndex*(window: Window): int = - discard - -proc imeCompositionString*(window: Window): string = - discard - -proc runeInputEnabled*(window: Window): bool = - discard - -proc `title=`*(window: Window, title: string) = - discard - -proc `visible=`*(window: Window, visible: bool) = - discard - -proc `decorated=`*(window: Window, decorated: bool) = - discard - -proc `resizable=`*(window: Window, resizable: bool) = - discard - -proc `fullscreen=`*(window: Window, fullscreen: bool) = - discard - -proc `size=`*(window: Window, size: IVec2) = - discard - -proc `pos=`*(window: Window, pos: IVec2) = - discard - -proc `minimized=`*(window: Window, minimized: bool) = - discard - -proc `maximized=`*(window: Window, maximized: bool) = - discard - -proc `closeRequested=`*(window: Window, closeRequested: bool) = - discard - -proc `runeInputEnabled=`*(window: Window, runeInputEnabled: bool) = - discard - -proc init*() = - discard - -proc pollEvents*() = - discard - -proc makeContextCurrent*(window: Window) = - discard - -proc swapBuffers*(window: Window) = - discard - -proc close*(window: Window) = - discard - -proc closeIme*(window: Window) = - discard - -proc newWindow*( - title: string, - size: IVec2, - visible = true, - vsync = true, - openglMajorVersion = 4, - openglMinorVersion = 1, - msaa = msaaDisabled, - depthBits = 24, - stencilBits = 8 -): Window = - discard - # @levovix0, you noted in the x11 file that window constructor can't set - # opengl version, msaa and stencilBits and args mustn't set depthBits directly - - # I am curious why this is, and if we can work to make those parameters work? - # If Discord would be easier, you can add me as guzba#7261 - -proc buttonDown*(window: Window): ButtonView = - discard - -proc buttonPressed*(window: Window): ButtonView = - discard - -proc buttonReleased*(window: Window): ButtonView = - discard - -proc buttonToggle*(window: Window): ButtonView = - discard - -proc getClipboardString*(): string = - discard - -proc setClipboardString*(value: string) = - discard From 12c2e0a78b29a30cfc07e80011584e9e0508784c Mon Sep 17 00:00:00 2001 From: levovix0 Date: Fri, 17 Dec 2021 00:54:28 +0300 Subject: [PATCH 07/14] center rect in fullscreen example to check resizing defects better --- examples/fullscreen.nim | 2 +- src/windy/platforms/linux/x11.nim | 2 +- src/windy/platforms/linux/x11/x.nim | 44 ++++++++++++++--------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/fullscreen.nim b/examples/fullscreen.nim index 4f10de6..bf0910e 100644 --- a/examples/fullscreen.nim +++ b/examples/fullscreen.nim @@ -10,7 +10,7 @@ let bxy = newBoxy() proc display() = bxy.beginFrame(window.size) bxy.drawRect(rect(vec2(0, 0), window.size.vec2), color(1, 1, 1, 1)) - bxy.drawRect(rect(vec2(100, 100), vec2(200, 200)), color(1, 0, 1, 1)) + bxy.drawRect(rect((window.size.vec2 / 2) - vec2(100, 100), vec2(200, 200)), color(1, 0, 1, 1)) bxy.endFrame() window.swapBuffers() diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index 464a60c..353ff72 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -318,7 +318,7 @@ proc `visible=`*(window: Window, v: bool) = proc size*(window: Window): IVec2 = - window.handle.geometry.size + window.prevSize proc `size=`*(window: Window, v: IVec2) = display.XResizeWindow(window.handle, v.x.uint32, v.y.uint32) diff --git a/src/windy/platforms/linux/x11/x.nim b/src/windy/platforms/linux/x11/x.nim index 38099ee..b93ff93 100644 --- a/src/windy/platforms/linux/x11/x.nim +++ b/src/windy/platforms/linux/x11/x.nim @@ -163,28 +163,28 @@ const LastExtensionError* = 255 InputOutput* = 1 InputOnly* = 2 - CWBackPixmap* = 1 shl 0 - CWBackPixel* = 1 shl 1 - CWBorderPixmap* = 1 shl 2 - CWBorderPixel* = 1 shl 3 - CWBitGravity* = 1 shl 4 - CWWinGravity* = 1 shl 5 - CWBackingStore* = 1 shl 6 - CWBackingPlanes* = 1 shl 7 - CWBackingPixel* = 1 shl 8 - CWOverrideRedirect* = 1 shl 9 - CWSaveUnder* = 1 shl 10 - CWEventMask* = 1 shl 11 - CWDontPropagate* = 1 shl 12 - CWColormap* = 1 shl 13 - CWCursor* = 1 shl 14 - CWX* = 1 shl 0 - CWY* = 1 shl 1 - CWWidth* = 1 shl 2 - CWHeight* = 1 shl 3 - CWBorderWidth* = 1 shl 4 - CWSibling* = 1 shl 5 - CWStackMode* = 1 shl 6 + CwBackPixmap* = 1 shl 0 + CwBackPixel* = 1 shl 1 + CwBorderPixmap* = 1 shl 2 + CwBorderPixel* = 1 shl 3 + CwBitGravity* = 1 shl 4 + CwWinGravity* = 1 shl 5 + CwBackingStore* = 1 shl 6 + CwBackingPlanes* = 1 shl 7 + CwBackingPixel* = 1 shl 8 + CwOverrideRedirect* = 1 shl 9 + CwSaveUnder* = 1 shl 10 + CwEventMask* = 1 shl 11 + CwDontPropagate* = 1 shl 12 + CwColormap* = 1 shl 13 + CwCursor* = 1 shl 14 + CwX* = 1 shl 0 + CwY* = 1 shl 1 + CwWidth* = 1 shl 2 + CwHeight* = 1 shl 3 + CwBorderWidth* = 1 shl 4 + CwSibling* = 1 shl 5 + CwStackMode* = 1 shl 6 ForgetGravity* = 0 NorthWestGravity* = 1 NorthGravity* = 2 From eb9a463836b337b7c22dd2ee5c392c44852bd251 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Sat, 18 Dec 2021 13:30:04 +0300 Subject: [PATCH 08/14] add basic frame synchronization on x11 --- src/windy/platforms/linux/x11.nim | 37 +++++++++++++++----------- src/windy/platforms/linux/x11/x.nim | 1 + src/windy/platforms/linux/x11/xlib.nim | 20 ++++++++++++++ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index 353ff72..a48b33a 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -34,6 +34,8 @@ type gc: GC ic: XIC im: XIM + xsyncConter: XSyncCounter + lastSync: XSyncValue closeRequested, closed: bool runeInputEnabled: bool @@ -97,16 +99,6 @@ proc init = initialized = true -proc geometry(window: XWindow): tuple[root: XWindow; pos, size: IVec2; borderW: int, depth: int] = - var - root: XWindow - x, y: int32 - w, h: uint32 - borderW: uint32 - depth: uint32 - display.XGetGeometry(window, root.addr, x.addr, y.addr, w.addr, h.addr, borderW.addr, depth.addr) - (root, ivec2(x, y), ivec2(w.int32, h.int32), borderW.int, depth.int) - proc property(window: XWindow, property: Atom): tuple[kind: Atom, data: string] = var kind: Atom @@ -289,10 +281,11 @@ proc queryKeyboardState(): set[0..255] = proc destroy(window: Window) = - if window.ic != nil: XDestroyIC(window.ic) - if window.im != nil: XCloseIM(window.im) - if window.gc != nil: display.XFreeGC(window.gc) - if window.handle != 0: display.XDestroyWindow(window.handle) + if window.ic != nil: XDestroyIC(window.ic) + if window.im != nil: XCloseIM(window.im) + if window.gc != nil: display.XFreeGC(window.gc) + if window.handle != 0: display.XDestroyWindow(window.handle) + if window.xsyncConter.int != 0: display.XSyncDestroyCounter(window.xsyncConter) wasMoved window[] window.closed = true @@ -501,7 +494,7 @@ proc newWindow*( ButtonReleaseMask or StructureNotifyMask or EnterWindowMask or LeaveWindowMask or FocusChangeMask ) - var wmProtocols = [atom"WM_DELETE_WINDOW"] + var wmProtocols = [atom"WM_DELETE_WINDOW", atom"_NET_WM_SYNC_REQUEST"] display.XSetWMProtocols(result.handle, wmProtocols[0].addr, cint wmProtocols.len) result.im = display.XOpenIM @@ -540,6 +533,14 @@ proc newWindow*( if visible: result.visible = true + block xsync: + var vEv, vEr: cint + if display.XSyncQueryExtension(vEv.addr, vEr.addr): + var vMaj, vMin: cint + display.XSyncInitialize(vMaj.addr, vMin.addr) + result.xsyncConter = display.XSyncCreateCounter(XSyncValue()) + result.handle.setProperty(atom"_NET_WM_SYNC_REQUEST_COUNTER", xaCardinal, 32, @[result.xsyncConter].asString) + windows.add result @@ -554,6 +555,9 @@ proc pollEvents(window: Window) = window.buttonPressed = {} window.buttonReleased = {} + # signal that frame was drawn + display.XSyncSetCounter(window.xsyncConter, window.lastSync) + var ev: XEvent proc checkEvent(d: Display, event: ptr XEvent, userData: pointer): bool {.cdecl.} = @@ -580,6 +584,9 @@ proc pollEvents(window: Window) = pushEvent onCloseRequest return # end polling events immediently + elif ev.client.data.l[0] == "_NET_WM_SYNC_REQUEST".atom.clong: + window.lastSync = XSyncValue(lo: cast[uint32](ev.client.data.l[2]), hi: cast[int32](ev.client.data.l[3])) + of xeFocusIn: if window.`"_focused"`: return # was duplicated window.`"_focused"` = true diff --git a/src/windy/platforms/linux/x11/x.nim b/src/windy/platforms/linux/x11/x.nim index b93ff93..19c1fba 100644 --- a/src/windy/platforms/linux/x11/x.nim +++ b/src/windy/platforms/linux/x11/x.nim @@ -14,6 +14,7 @@ type Colormap* = XID GContext* = XID KeySym* = XID + XSyncCounter* = distinct XID KeyCode* = cuchar diff --git a/src/windy/platforms/linux/x11/xlib.nim b/src/windy/platforms/linux/x11/xlib.nim index 478a740..15071fd 100644 --- a/src/windy/platforms/linux/x11/xlib.nim +++ b/src/windy/platforms/linux/x11/xlib.nim @@ -4,6 +4,10 @@ const libX11* = when defined(macosx): "libX11.dylib" else: "libX11.so(|.6)" +const libXExt* = + when defined(macosx): "libXext.dylib" + else: "libXext.so(|.6)" + type Display* = ptr object ext_data*: ptr XExtData @@ -173,6 +177,10 @@ type minAspect*, maxAspect*: IVec2 baseSize*: IVec2 winGravity*: cint + + XSyncValue* = object + hi*: int32 + lo*: uint32 const XIMPreeditArea* = 1 shl 0 @@ -315,3 +323,15 @@ proc XSetSelectionOwner*(d; kind: Atom, window: Window, time: int32 = CurrentTim proc XConvertSelection*(d; kind: Atom, to: Atom, resultProperty: Atom, window: Window, time: int32 = CurrentTime) {.pop.} + +{.push, cdecl, dynlib: libXExt, importc.} + +proc XSyncQueryExtension*(d; vEv, vEr: ptr cint): bool +proc XSyncInitialize*(d; verMaj, verMin: ptr cint) + +proc XSyncCreateCounter*(d; v: XSyncValue): XSyncCounter +proc XSyncDestroyCounter*(d; c: XSyncCounter) + +proc XSyncSetCounter*(d; c: XSyncCounter, v: XSyncValue) + +{.pop.} From c5d513fc9e4ea61a3f2f8538472d0c9b1b7a1f49 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Sat, 18 Dec 2021 19:30:37 +0300 Subject: [PATCH 09/14] wayland opening very simple window --- src/windy/platforms/linux/wayland.nim | 26 +++++++++- src/windy/platforms/linux/wayland/basic.nim | 49 +++++++++++++++++-- .../platforms/linux/wayland/protocol.nim | 12 ++--- .../platforms/linux/wayland/sharedBuffer.nim | 35 +++++++++++++ 4 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 src/windy/platforms/linux/wayland/sharedBuffer.nim diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index e35a44e..f129144 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -1,5 +1,6 @@ +import vmath import ../../common, ../../internal -import wayland/protocol +import wayland/[protocol, sharedBuffer] var initialized: bool @@ -53,3 +54,26 @@ proc init* = sync display initialized = true + +when isMainModule: + init() + echo shmFormats + let srf = compositor.newSurface + let ssrf = shell.shellSurface(srf) + let tl = ssrf.toplevel + + commit srf + + ssrf.onConfigure: + ssrf.ackConfigure(serial) + commit srf + + tl.onClose: quit() + + sync display + + let buf = shm.create(ivec2(128, 128), ShmFormat.xrgb8888) + attach srf, buf.buffer, ivec2(0, 0) + commit srf + + while true: sync display diff --git a/src/windy/platforms/linux/wayland/basic.nim b/src/windy/platforms/linux/wayland/basic.nim index d6d56e1..99c9491 100644 --- a/src/windy/platforms/linux/wayland/basic.nim +++ b/src/windy/platforms/linux/wayland/basic.nim @@ -1,4 +1,4 @@ -import os, posix, nativesockets, net, tables +import os, posix, nativesockets, net, tables, sequtils import ../../../common type @@ -88,7 +88,7 @@ proc destroy*(x: Proxy) = proc serialize[T](x: T): seq[uint32] = - when x is uint32|int32|Id|enum|float32|FileDescriptor: + when x is uint32|int32|Id|enum|float32: result.add cast[uint32](x) elif x is int: @@ -124,6 +124,8 @@ proc serialize[T](x: T): seq[uint32] = elif x is Proxy: result.add x.id.uint32 + + elif x is FileDescriptor: discard # will be stored in the ancillary data of the UNIX domain socket message (msg_control) elif T.sizeof == uint32.sizeof: result.add cast[uint32](x) @@ -131,8 +133,20 @@ proc serialize[T](x: T): seq[uint32] = else: {.error: "unserializable type " & $T.} +proc fileDescriptors[T](x: T): seq[FileDescriptor] = + when x is FileDescriptor: result.add x + + elif x is seq|array: + for x in x: + result.add fileDescriptors(x) + + elif x is tuple|object: + for x in x.fields: + result.add fileDescriptors(x) + + proc deserialize(display: Display, x: seq[uint32], T: type, i: var uint32): T = - when result is uint32|int32|Id|enum|float32|FileDescriptor: + when result is uint32|int32|Id|enum|float32: result = cast[T](x[i]); i += 1 elif result is int: @@ -168,6 +182,9 @@ proc deserialize(display: Display, x: seq[uint32], T: type, i: var uint32): T = when T.sizeof > uint.sizeof: {.error: "too large set".} result = cast[T](x[i]); i += 1 + elif result is FileDescriptor: + ## todo + elif T.sizeof == uint32.sizeof: result = cast[T](x[i]); i += 1 @@ -186,7 +203,31 @@ proc marshal[T](x: Proxy, op: int, data: T = ()) = var d = data.serialize d.insert ((d.len.uint32 * uint32.sizeof.uint32 + 8) shl 16) or (op.uint32 and 0x0000ffff) d.insert x.id.uint32 - assert x.display.socket.send(d[0].addr, d.len * uint32.sizeof) == d.len * uint32.sizeof + + let fds = data.fileDescriptors + + var iovec = IOVec( + iov_base: d[0].addr, + iov_len: csize_t d.len * uint32.sizeof, + ) + + var hdr = 0.cint.repeat(csize_t.sizeof div cint.sizeof) & @[SOL_SOCKET, SCM_RIGHTS] & cast[seq[cint]](fds) + cast[ptr csize_t](hdr[0].addr)[] = csize_t hdr.len * cint.sizeof + + var msg = Tmsghdr( + msg_iov: iovec.addr, + msg_iovlen: 1, + msg_control: + if fds.len == 0: nil + else: hdr[0].addr, + msg_controllen: + if fds.len == 0: 0.csize_t + else: csize_t hdr.len * cint.sizeof + ) + + let len = x.display.socket.getFd.sendmsg(msg.addr, 0x4000) + assert len == d.len * uint32.sizeof + method unmarshal(x: Proxy, op: int, data: seq[uint32]) {.base, locks: "unknown".} = discard diff --git a/src/windy/platforms/linux/wayland/protocol.nim b/src/windy/platforms/linux/wayland/protocol.nim index 0dd091a..7257f75 100644 --- a/src/windy/platforms/linux/wayland/protocol.nim +++ b/src/windy/platforms/linux/wayland/protocol.nim @@ -363,12 +363,6 @@ protocol: proc newRegion: Region - ShmPool: - proc newBuffer(offset: int, size: IVec2, stride: int, format: ShmFormat): Buffer - proc destroy - proc resize(size: int) - - Shm: iface "wl_shm" @@ -377,6 +371,12 @@ protocol: proc format(format: ShmFormat): event + ShmPool: + proc newBuffer(offset: int, size: IVec2, stride: int, format: ShmFormat): Buffer + proc destroy + proc resize(size: int) + + Buffer: proc destroy diff --git a/src/windy/platforms/linux/wayland/sharedBuffer.nim b/src/windy/platforms/linux/wayland/sharedBuffer.nim new file mode 100644 index 0000000..43b06ec --- /dev/null +++ b/src/windy/platforms/linux/wayland/sharedBuffer.nim @@ -0,0 +1,35 @@ +import memfiles, os +import vmath +import protocol + +type SharedBuffer* = ref object + shm: Shm + buffer: Buffer + file: MemFile + filename: string + +proc dataAddr*(buffer: SharedBuffer): pointer = + buffer.file.mem + +proc fileDescriptor*(buffer: SharedBuffer): FileDescriptor = + buffer.file.handle.FileDescriptor + +proc buffer*(buffer: SharedBuffer): Buffer = + buffer.buffer + +proc create*(shm: Shm, size: IVec2, format: ShmFormat): SharedBuffer = + new result, proc(buffer: SharedBuffer) = + close buffer.file + + result.shm = shm + + let filebase = getEnv("XDG_RUNTIME_DIR") / "windy-" + for i in 0..int.high: + if not fileExists(filebase & $i): + result.filename = filebase & $i + result.file = memfiles.open(result.filename, mode=fmReadWrite, allowRemap=true, newFileSize = size.x * size.y * 4) + break + + let pool = shm.newPool(result.fileDescriptor, size.x * size.y * 4) + result.buffer = pool.newBuffer(0, size, size.x * 4, format) + destroy pool From 1621ef392d09067ed361577672331e9af05754fd Mon Sep 17 00:00:00 2001 From: levovix0 Date: Sat, 25 Dec 2021 12:49:07 +0300 Subject: [PATCH 10/14] start adding opengl support to wayland window --- src/windy/platforms/linux/wayland/basic.nim | 2 + src/windy/platforms/linux/wayland/egl.nim | 93 +++++++++++++++++++ .../platforms/linux/wayland/sharedBuffer.nim | 1 + 3 files changed, 96 insertions(+) create mode 100644 src/windy/platforms/linux/wayland/egl.nim diff --git a/src/windy/platforms/linux/wayland/basic.nim b/src/windy/platforms/linux/wayland/basic.nim index 99c9491..2be8eea 100644 --- a/src/windy/platforms/linux/wayland/basic.nim +++ b/src/windy/platforms/linux/wayland/basic.nim @@ -1,3 +1,5 @@ +## note: this file is included in protocol.nim, don't import it directly + import os, posix, nativesockets, net, tables, sequtils import ../../../common diff --git a/src/windy/platforms/linux/wayland/egl.nim b/src/windy/platforms/linux/wayland/egl.nim new file mode 100644 index 0000000..ad513f4 --- /dev/null +++ b/src/windy/platforms/linux/wayland/egl.nim @@ -0,0 +1,93 @@ +import strutils +import vmath +import ../../../common +import protocol, sharedBuffer + +type + EglDisplay = ptr object + EglConfig = ptr object + EglSurface = ptr object + EglContext = ptr object + + EglApi {.pure, size: 4.} = enum + openglEs = 0x30A0 + openvg = 0x30A1 + opengl = 0x30A2 + +const + eglExtensions = int32 0x3055 + + eglSurfaceType = int32 0x3033 + eglWindowBit = int32 0x0004 + + eglRenderableType = int32 0x3040 + eglOpenglEs2Bit = int32 0x0004 + + eglAlphaSize = int32 0x3021 + eglBlueSize = int32 0x3022 + eglGreenSize = int32 0x3023 + eglRedSize = int32 0x3024 + +{.push, cdecl, dynlib: "libEGL.so(|.1)", importc.} + +using d: EglDisplay + +proc eglBindAPI(api: EglApi): bool +proc eglGetDisplay(native: pointer = nil): EglDisplay + +proc eglInitialize(d; major: ptr int32 = nil, minor: ptr int32 = nil): bool +proc eglTerminate(d) + +proc eglQueryString(d; n: int32): cstring + +proc eglGetConfigs(d; retCfgs: ptr EglConfig, cfgSize: int32, retCfgCount: ptr int32): bool +proc eglChooseConfig(d; attrs: ptr int32, retCfgs: ptr EglConfig, cfgSize: int32, retCfgCount: ptr int32): bool + +proc eglCreateContext(d; config: EglConfig, share: EglContext = nil, attrs: ptr int32 = nil): EglContext + +proc eglCreateWindowSurface(d; config: EglConfig, win: pointer, attrs: ptr int32 = nil): EglSurface +proc eglCreatePbufferSurface(d; config: EglConfig, attrs: ptr int32 = nil): EglSurface +proc eglCreatePbufferFromClientBuffer(d; kind: uint32, buffer: pointer, config: EglConfig, attrs: ptr int32 = nil): EglSurface + +{.pop.} + + +when isMainModule: + template expect(x) = + if not x: raise WindyError.newException(astToStr(x) & ": Error creating OpenGL ES context") + + expect eglBindAPI(EglApi.openglEs) + let d = eglGetDisplay() + expect d != nil + + expect d.eglInitialize + + # echo d.eglQueryString(eglExtensions).`$`.split + + var + config: EglConfig + configCount: int32 + var attrs = [ + eglSurfaceType, eglWindowBit, + eglRenderableType, eglOpenglEs2Bit, + eglRedSize, 8, + eglGreenSize, 8, + eglBlueSize, 8, + # eglAlphaSize, 8, + 0 + ] + # expect d.eglChooseConfig(attrs[0].addr, config.addr, 1, configCount.addr) + expect d.eglGetConfigs(config.addr, 1, configCount.addr) + expect configCount == 1 + + let ctx = d.eglCreateContext(config) + + # var i = (alloc(8), 1, 1) + + echo d.eglCreateWindowSurface(config, nil).repr + # echo d.eglCreatePbufferSurface(config).repr + + + # while true: sync display + + d.eglTerminate diff --git a/src/windy/platforms/linux/wayland/sharedBuffer.nim b/src/windy/platforms/linux/wayland/sharedBuffer.nim index 43b06ec..7a5c344 100644 --- a/src/windy/platforms/linux/wayland/sharedBuffer.nim +++ b/src/windy/platforms/linux/wayland/sharedBuffer.nim @@ -3,6 +3,7 @@ import vmath import protocol type SharedBuffer* = ref object + ## memmaped file that can be shared between processes shm: Shm buffer: Buffer file: MemFile From c462b95c191ea860cb8d86df514d4c0900349645 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Sat, 25 Dec 2021 13:48:52 +0300 Subject: [PATCH 11/14] rm decorated and resizable, add style --- src/windy/platforms/linux/x11.nim | 104 ++++++++++++++++++------------ 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index a48b33a..590c530 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -360,15 +360,48 @@ proc focus*(window: Window) = display.XSetInputFocus(window.handle, rtNone) -proc decorated*(window: Window): bool = - window.`"_decorated"` +proc fullscreen*(window: Window): bool = + atom"_NET_WM_STATE_FULLSCREEN" in window.handle.wmState + +proc `fullscreen=`*(window: Window, v: bool) = + window.handle.wmStateSend v.int, atom"_NET_WM_STATE_FULLSCREEN" + -proc `decorated=`*(window: Window, v: bool) = - window.`"_decorated"` = v +proc style*(window: Window): WindowStyle = + if window.`"_decorated"`: + var hints: XSizeHints + display.XGetNormalHints(window.handle, hints.addr) + if (hints.flags and 0b110000) == 0b110000: + WindowStyle.Decorated + else: WindowStyle.DecoratedResizable + else: WindowStyle.Undecorated - let size = window.size # save current window size +proc `style=`*(window: Window, v: WindowStyle) = + if window.fullscreen: return + + let currentStyle = window.style + if currentStyle == v: return + + template addDecorations {.dirty.} = + window.`"_decorated"` = true + case wmForDecoratedKind + of WmForDecoratedKind.motiv, WmForDecoratedKind.kwm, WmForDecoratedKind.other: + window.handle.delProperty(decoratedAtom) + + display.XSetTransientForHint(window.handle, 0) + if window.visible: + # "reopen" window + display.XUnmapWindow(window.handle) + display.XMapWindow(window.handle) + + window.size = size # restore window size + else: discard + + case v + of WindowStyle.Undecorated: + window.`"_decorated"` = false + let size = window.size # save current window size - if not v: case wmForDecoratedKind of WmForDecoratedKind.motiv: window.handle.setProperty(decoratedAtom, decoratedAtom, 32, @[1 shl 1, 0, 0, 0, 0].asString) @@ -381,43 +414,30 @@ proc `decorated=`*(window: Window, v: bool) = # "reopen" window display.XUnmapWindow(window.handle) display.XMapWindow(window.handle) - - else: - case wmForDecoratedKind - of WmForDecoratedKind.motiv, WmForDecoratedKind.kwm, WmForDecoratedKind.other: - window.handle.delProperty(decoratedAtom) - else: return - display.XSetTransientForHint(window.handle, 0) - if window.visible: - # "reopen" window - display.XUnmapWindow(window.handle) - display.XMapWindow(window.handle) - - window.size = size # restore window size - - -proc resizable*(window: Window): bool = - let size = window.size - var hints: XSizeHints - display.XGetNormalHints(window.handle, hints.addr) - hints.minSize == size and hints.maxSize == size - -proc `resizable=`*(window: Window, v: bool) = - let size = window.size - var hints = XSizeHints( - flags: (1 shl 4) or (1 shl 5), - minSize: size, - maxSize: size - ) - display.XSetNormalHints(window.handle, hints.addr) - - -proc fullscreen*(window: Window): bool = - atom"_NET_WM_STATE_FULLSCREEN" in window.handle.wmState - -proc `fullscreen=`*(window: Window, v: bool) = - window.handle.wmStateSend v.int, atom"_NET_WM_STATE_FULLSCREEN" + window.size = size # restore window size + + of WindowStyle.Decorated: + let size = window.size + + if currentStyle == WindowStyle.Undecorated: addDecorations + + # make window unresizable + var hints = XSizeHints( + flags: 0b110000, + minSize: size, + maxSize: size + ) + display.XSetNormalHints(window.handle, hints.addr) + + of WindowStyle.DecoratedResizable: + let size = window.size + + if currentStyle == WindowStyle.Undecorated: addDecorations + + # make window resizable + var hints = XSizeHints(flags: 0) + display.XSetNormalHints(window.handle, hints.addr) proc title*(window: Window): string = From d80c06bbaab67ba0d47c9316592cdc2265838e55 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Sat, 25 Dec 2021 23:37:35 +0300 Subject: [PATCH 12/14] add wl_drm protocol --- src/windy/platforms/linux/wayland.nim | 13 ++-- src/windy/platforms/linux/wayland/egl.nim | 61 +++++++++++++---- .../platforms/linux/wayland/protocol.nim | 66 ++++++++++++++++--- .../platforms/linux/wayland/sharedBuffer.nim | 2 +- 4 files changed, 112 insertions(+), 30 deletions(-) diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index f129144..b8478e6 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -12,7 +12,7 @@ var shm: Shm shell: XdgWmBase - shmFormats: seq[ShmFormat] + pixelFormats: seq[PixelFormat] proc init* = @@ -26,16 +26,16 @@ proc init* = registry.onGlobal: case iface - of "wl_compositor": + of Compositor.iface: compositor = registry.bindInterface(Compositor, name, iface, version) - of "wl_shm": + of Shm.iface: shm = registry.bindInterface(Shm, name, iface, version) shm.onFormat: - shmFormats.add format + pixelFormats.add format - of "xdg_wm_base": + of XdgWmBase.iface: shell = registry.bindInterface(XdgWmBase, name, iface, version) shell.onPing: @@ -57,7 +57,6 @@ proc init* = when isMainModule: init() - echo shmFormats let srf = compositor.newSurface let ssrf = shell.shellSurface(srf) let tl = ssrf.toplevel @@ -72,7 +71,7 @@ when isMainModule: sync display - let buf = shm.create(ivec2(128, 128), ShmFormat.xrgb8888) + let buf = shm.create(ivec2(128, 128), PixelFormat.xrgb8888) attach srf, buf.buffer, ivec2(0, 0) commit srf diff --git a/src/windy/platforms/linux/wayland/egl.nim b/src/windy/platforms/linux/wayland/egl.nim index ad513f4..ca3618c 100644 --- a/src/windy/platforms/linux/wayland/egl.nim +++ b/src/windy/platforms/linux/wayland/egl.nim @@ -13,25 +13,48 @@ type openglEs = 0x30A0 openvg = 0x30A1 opengl = 0x30A2 + + EglError {.pure, size: 4.} = enum + badAccess = 0x3002 + badAlloc = 0x3003 + badAttribute = 0x3004 + badConfig = 0x3005 + badContext = 0x3006 + badCurrentSurface = 0x3007 + badDisplay = 0x3008 + badMatch = 0x3009 + badNativePixmap = 0x300A + badNativeWindow = 0x300B + badParameter = 0x300C + badSurface = 0x300D const eglExtensions = int32 0x3055 eglSurfaceType = int32 0x3033 + eglPBufferBit = int32 0x0001 eglWindowBit = int32 0x0004 eglRenderableType = int32 0x3040 eglOpenglEs2Bit = int32 0x0004 - eglAlphaSize = int32 0x3021 - eglBlueSize = int32 0x3022 - eglGreenSize = int32 0x3023 - eglRedSize = int32 0x3024 + eglAlphaSize = int32 0x3021 + eglBlueSize = int32 0x3022 + eglGreenSize = int32 0x3023 + eglRedSize = int32 0x3024 + + eglWidth = int32 0x3057 + eglHeight = int32 0x3056 + + eglOpenVGImage = uint32 0x3096 + + eglNone = int32 0x3038 {.push, cdecl, dynlib: "libEGL.so(|.1)", importc.} using d: EglDisplay +proc eglGetError(): EglError proc eglBindAPI(api: EglApi): bool proc eglGetDisplay(native: pointer = nil): EglDisplay @@ -40,8 +63,8 @@ proc eglTerminate(d) proc eglQueryString(d; n: int32): cstring -proc eglGetConfigs(d; retCfgs: ptr EglConfig, cfgSize: int32, retCfgCount: ptr int32): bool -proc eglChooseConfig(d; attrs: ptr int32, retCfgs: ptr EglConfig, cfgSize: int32, retCfgCount: ptr int32): bool +proc eglGetConfigs(d; retConfigs: ptr EglConfig, maxConfigs: int32, retCfgCount: ptr int32): bool +proc eglChooseConfig(d; attrs: ptr int32, retConfigs: ptr EglConfig, maxConfigs: int32, retConfigCount: ptr int32): bool proc eglCreateContext(d; config: EglConfig, share: EglContext = nil, attrs: ptr int32 = nil): EglContext @@ -54,7 +77,7 @@ proc eglCreatePbufferFromClientBuffer(d; kind: uint32, buffer: pointer, config: when isMainModule: template expect(x) = - if not x: raise WindyError.newException(astToStr(x) & ": Error creating OpenGL ES context") + if not x: raise WindyError.newException(astToStr(x) & ": Error creating OpenGL context (" & $eglGetError() & ")") expect eglBindAPI(EglApi.openglEs) let d = eglGetDisplay() @@ -68,25 +91,35 @@ when isMainModule: config: EglConfig configCount: int32 var attrs = [ - eglSurfaceType, eglWindowBit, + eglSurfaceType, eglPBufferBit, eglRenderableType, eglOpenglEs2Bit, eglRedSize, 8, eglGreenSize, 8, eglBlueSize, 8, # eglAlphaSize, 8, - 0 + eglNone ] - # expect d.eglChooseConfig(attrs[0].addr, config.addr, 1, configCount.addr) - expect d.eglGetConfigs(config.addr, 1, configCount.addr) + expect d.eglChooseConfig(attrs[0].addr, config.addr, 1, configCount.addr) + # expect d.eglGetConfigs(config.addr, 1, configCount.addr) expect configCount == 1 let ctx = d.eglCreateContext(config) + expect ctx != nil - # var i = (alloc(8), 1, 1) + var buff = alloc(32*32*4*8) + + var winAttrs = [ + eglWidth, 32, + eglHeight, 32, + eglNone + ] - echo d.eglCreateWindowSurface(config, nil).repr - # echo d.eglCreatePbufferSurface(config).repr + # let win = d.eglCreateWindowSurface(config, i.addr) + # let win = d.eglCreatePbufferSurface(config, winAttrs[0].addr) + let win = d.eglCreatePbufferFromClientBuffer(0x308E, buff, config, winAttrs[0].addr) + expect win != nil + # while true: sync display diff --git a/src/windy/platforms/linux/wayland/protocol.nim b/src/windy/platforms/linux/wayland/protocol.nim index 7257f75..12052d7 100644 --- a/src/windy/platforms/linux/wayland/protocol.nim +++ b/src/windy/platforms/linux/wayland/protocol.nim @@ -16,6 +16,37 @@ proc fromBitfield(x: int, t: type[Option]): t = else: some x.unshl.T macro protocol(body) = + ## transforms + ## T: + ## iface "wl_t" + ## + ## proc f(a: int): R + ## proc e(a: int): event + ## + ## to + ## type + ## T* = ref object of Proxy + ## e*: proc (a: int) + ## + ## proc iface*(t: type T): string = "wl_t" + ## + ## proc f*(this: T; a: int): R = + ## result = new(this.display, R) + ## marshal(this, 0, (result.id, a)) + ## + ## template onE*(x: T; body) = + ## x.e = proc (a {.inject.}: int) = + ## body + ## + ## method unmarshal(this: T; op: int; data: seq[uint32]) {.locks: "unknown".} = + ## case op + ## of 0: + ## if this.e != nil: + ## let (a) = deserialize(this.display, data, (int,)) + ## this.e(a) + ## else: + ## discard + proc injectAllParams(x: NimNode): NimNode = result = nnkFormalParams.newTree( @[x[0]] & @@ -55,7 +86,10 @@ macro protocol(body) = for a in x[1]: if a.kind == nnkCommand and a[0] == ident"iface": - continue #TODO: auto iface require + let l = a[1] + result.add quote do: + proc iface*(t: typedesc[`t`]): string = `l` + continue let p = a.params @@ -177,7 +211,7 @@ macro protocol(body) = type - ShmFormat* {.pure.} = enum + PixelFormat* {.pure.} = enum argb8888 = 0 xrgb8888 = 1 c8 = 0x20203843 @@ -264,9 +298,9 @@ type rotated180 rotated270 flipped - rotated90_and_flipped - rotated180_and_flipped - rotated270_and_flipped + flippedRotated90 + flippedRotated180 + flippedRotated270 Capability* {.pure.} = enum cursor @@ -333,7 +367,9 @@ type tiledRight tiledTop tiledBottom - + + DrmCapability* {.pure.} = enum + prime = 1 protocol: @@ -368,11 +404,11 @@ protocol: proc newPool(fd: FileDescriptor, size: int): ShmPool - proc format(format: ShmFormat): event + proc format(format: PixelFormat): event ShmPool: - proc newBuffer(offset: int, size: IVec2, stride: int, format: ShmFormat): Buffer + proc newBuffer(offset: int, size: IVec2, stride: int, format: PixelFormat): Buffer proc destroy proc resize(size: int) @@ -589,6 +625,20 @@ protocol: proc configure(pos: IVec2, size: IVec2): event proc done: event proc repositioned(token: int): event + + + Drm: + iface "wl_drm" + + proc init(id: int) + proc newBuffer(name: int, size: IVec2, stride: int, format: PixelFormat): Buffer + proc newPlanarBuffer(name: int, size: IVec2, format: PixelFormat, offsetsAndStrides: array[3, (int, int)]): Buffer + proc newPrimeBuffer(name: FileDescriptor, size: IVec2, format: PixelFormat, offsetsAndStrides: array[3, (int, int)]): Buffer + + proc device(name: string): event + proc format(format: PixelFormat): event + proc inited: event + proc capabilities(capable: set[DrmCapability]): event diff --git a/src/windy/platforms/linux/wayland/sharedBuffer.nim b/src/windy/platforms/linux/wayland/sharedBuffer.nim index 7a5c344..05e65a3 100644 --- a/src/windy/platforms/linux/wayland/sharedBuffer.nim +++ b/src/windy/platforms/linux/wayland/sharedBuffer.nim @@ -18,7 +18,7 @@ proc fileDescriptor*(buffer: SharedBuffer): FileDescriptor = proc buffer*(buffer: SharedBuffer): Buffer = buffer.buffer -proc create*(shm: Shm, size: IVec2, format: ShmFormat): SharedBuffer = +proc create*(shm: Shm, size: IVec2, format: PixelFormat): SharedBuffer = new result, proc(buffer: SharedBuffer) = close buffer.file From 8a72734f5f9865030757781fbfbc73c68d67a285 Mon Sep 17 00:00:00 2001 From: levovix0 Date: Mon, 27 Dec 2021 14:52:47 +0300 Subject: [PATCH 13/14] creating opengl context on wayland --- src/windy/platforms/linux/wayland.nim | 13 ++++- src/windy/platforms/linux/wayland/egl.nim | 69 ++++++++++------------- 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index b8478e6..c769723 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -1,6 +1,6 @@ -import vmath +import vmath, opengl import ../../common, ../../internal -import wayland/[protocol, sharedBuffer] +import wayland/[protocol, sharedBuffer, egl] var initialized: bool @@ -53,6 +53,8 @@ proc init* = sync display + initEgl() + initialized = true when isMainModule: @@ -75,4 +77,11 @@ when isMainModule: attach srf, buf.buffer, ivec2(0, 0) commit srf + makeCurrent newOpenglContext() + + loadExtensions() + + var fbo: uint32 + glGenFramebuffers(1, fbo.addr) + while true: sync display diff --git a/src/windy/platforms/linux/wayland/egl.nim b/src/windy/platforms/linux/wayland/egl.nim index ca3618c..c73283e 100644 --- a/src/windy/platforms/linux/wayland/egl.nim +++ b/src/windy/platforms/linux/wayland/egl.nim @@ -1,19 +1,16 @@ -import strutils import vmath import ../../../common -import protocol, sharedBuffer type + OpenglContext* = object + ctx: EglContext + srf: EglSurface + EglDisplay = ptr object EglConfig = ptr object EglSurface = ptr object EglContext = ptr object - EglApi {.pure, size: 4.} = enum - openglEs = 0x30A0 - openvg = 0x30A1 - opengl = 0x30A2 - EglError {.pure, size: 4.} = enum badAccess = 0x3002 badAlloc = 0x3003 @@ -28,12 +25,10 @@ type badParameter = 0x300C badSurface = 0x300D -const - eglExtensions = int32 0x3055 +const eglSurfaceType = int32 0x3033 eglPBufferBit = int32 0x0001 - eglWindowBit = int32 0x0004 eglRenderableType = int32 0x3040 eglOpenglEs2Bit = int32 0x0004 @@ -46,47 +41,44 @@ const eglWidth = int32 0x3057 eglHeight = int32 0x3056 - eglOpenVGImage = uint32 0x3096 - eglNone = int32 0x3038 + {.push, cdecl, dynlib: "libEGL.so(|.1)", importc.} using d: EglDisplay proc eglGetError(): EglError -proc eglBindAPI(api: EglApi): bool proc eglGetDisplay(native: pointer = nil): EglDisplay proc eglInitialize(d; major: ptr int32 = nil, minor: ptr int32 = nil): bool proc eglTerminate(d) -proc eglQueryString(d; n: int32): cstring - -proc eglGetConfigs(d; retConfigs: ptr EglConfig, maxConfigs: int32, retCfgCount: ptr int32): bool proc eglChooseConfig(d; attrs: ptr int32, retConfigs: ptr EglConfig, maxConfigs: int32, retConfigCount: ptr int32): bool proc eglCreateContext(d; config: EglConfig, share: EglContext = nil, attrs: ptr int32 = nil): EglContext -proc eglCreateWindowSurface(d; config: EglConfig, win: pointer, attrs: ptr int32 = nil): EglSurface proc eglCreatePbufferSurface(d; config: EglConfig, attrs: ptr int32 = nil): EglSurface -proc eglCreatePbufferFromClientBuffer(d; kind: uint32, buffer: pointer, config: EglConfig, attrs: ptr int32 = nil): EglSurface + +proc eglMakeCurrent(d; draw, read: EglSurface, ctx: EglContext): bool {.pop.} -when isMainModule: - template expect(x) = - if not x: raise WindyError.newException(astToStr(x) & ": Error creating OpenGL context (" & $eglGetError() & ")") +template expect(x) = + if not x: raise WindyError.newException("Error creating OpenGL context (" & $eglGetError() & ")") - expect eglBindAPI(EglApi.openglEs) - let d = eglGetDisplay() - expect d != nil +var d: EglDisplay + +proc initEgl* = + d = eglGetDisplay() + expect d != nil expect d.eglInitialize - # echo d.eglQueryString(eglExtensions).`$`.split +proc newOpenglContext*: OpenglContext = + ## creates opengl context (on new dummy surface) var config: EglConfig configCount: int32 @@ -96,31 +88,28 @@ when isMainModule: eglRedSize, 8, eglGreenSize, 8, eglBlueSize, 8, - # eglAlphaSize, 8, + eglAlphaSize, 8, eglNone ] expect d.eglChooseConfig(attrs[0].addr, config.addr, 1, configCount.addr) - # expect d.eglGetConfigs(config.addr, 1, configCount.addr) expect configCount == 1 - let ctx = d.eglCreateContext(config) - expect ctx != nil - - var buff = alloc(32*32*4*8) + result.ctx = d.eglCreateContext(config) + expect result.ctx != nil - var winAttrs = [ - eglWidth, 32, - eglHeight, 32, + var attrs2 = [ + eglWidth, 1, + eglHeight, 1, eglNone ] + result.srf = d.eglCreatePbufferSurface(config, attrs2[0].addr) + expect result.srf != nil - # let win = d.eglCreateWindowSurface(config, i.addr) - # let win = d.eglCreatePbufferSurface(config, winAttrs[0].addr) - let win = d.eglCreatePbufferFromClientBuffer(0x308E, buff, config, winAttrs[0].addr) - expect win != nil - +proc makeCurrent*(context: OpenglContext) = + if not d.eglMakeCurrent(context.srf, context.srf, context.ctx): + raise WindyError.newException("Error creating OpenGL context (" & $eglGetError() & ")") - # while true: sync display +proc terminateEgl* = d.eglTerminate From 7f68aa306fd18afbfd49f878959cc4aaba61024a Mon Sep 17 00:00:00 2001 From: levovix0 Date: Tue, 28 Dec 2021 15:00:54 +0300 Subject: [PATCH 14/14] f --- src/windy/platforms/linux/wayland.nim | 12 +++++++----- src/windy/platforms/linux/wayland/protocol.nim | 8 ++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index c769723..e4da226 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -1,4 +1,4 @@ -import vmath, opengl +import vmath import ../../common, ../../internal import wayland/[protocol, sharedBuffer, egl] @@ -79,9 +79,11 @@ when isMainModule: makeCurrent newOpenglContext() - loadExtensions() - - var fbo: uint32 - glGenFramebuffers(1, fbo.addr) + # how to draw on window? + # i tried: + # creating context on window (incompatible native window (wl_window vs. protocol.Window)) + # eglCreateDRMImageMESA/eglExportDRMImageMESA/wl_drm.newBuffer (fails via BadAlloc) + # in this code works: + # setting pixels manually on buf.dataAddr (no OpenGL) while true: sync display diff --git a/src/windy/platforms/linux/wayland/protocol.nim b/src/windy/platforms/linux/wayland/protocol.nim index 12052d7..2f5c97e 100644 --- a/src/windy/platforms/linux/wayland/protocol.nim +++ b/src/windy/platforms/linux/wayland/protocol.nim @@ -103,8 +103,12 @@ macro protocol(body) = ), newEmptyNode() ) + + let onei = ident &"on{($a.name).runeAt(0).toUpper}{($a.name).toRunes[1..^1]}" + onei.copyLineInfo a.name + result.add nnkTemplateDef.newTree( # x.onEvent:... template - nnkPostfix.newTree(ident"*", ident &"on{($a.name).runeAt(0).toUpper}{($a.name).toRunes[1..^1]}"), + nnkPostfix.newTree(ident"*", onei), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( @@ -638,7 +642,7 @@ protocol: proc device(name: string): event proc format(format: PixelFormat): event proc inited: event - proc capabilities(capable: set[DrmCapability]): event + proc capabilities(capabilities: set[DrmCapability]): event