diff --git a/examples/property_changes.nim b/examples/property_changes.nim new file mode 100644 index 0000000..7477d97 --- /dev/null +++ b/examples/property_changes.nim @@ -0,0 +1,69 @@ +import opengl, windy, os + +let window = newWindow("Windy Properties", ivec2(1280, 800)) + +window.makeContextCurrent() +loadExtensions() + +proc display() = + glClear(GL_COLOR_BUFFER_BIT) + # Your OpenGL display code here + window.swapBuffers() + +while not window.closeRequested: + display() + + sleep(100) + + pollEvents() + + # Window will block changing size. + window.size = ivec2(300, 400) + doAssert window.size == ivec2(300, 400) + + # Not all sizes are valid, window try to go max it can. + window.size = ivec2(300, 30000) + doAssert window.size.y != 30000 # You can't get height 30000 even if you ask. + doAssert window.size != ivec2(300, 400) + + # Change size back. + window.size = ivec2(300, 400) + doAssert window.size == ivec2(300, 400) + + window.pos = ivec2(200, 100) + doAssert window.pos == ivec2(200, 100) + + window.pos = ivec2(-9000, 100) + when defined(windows): + # On Windows it is possible to set window to invalid locations. + doAssert window.pos.x == -9000 + else: + doAssert window.pos.x != -9000 + doAssert window.pos != ivec2(200, 100) + + window.maximized = false + doAssert not window.maximized + + window.maximized = true + doAssert window.maximized + + window.maximized = false + doAssert not window.maximized + + window.minimized = false + doAssert not window.minimized + + window.minimized = true + doAssert window.minimized + + window.fullscreen = false + doAssert not window.fullscreen + + window.fullscreen = true + doAssert window.fullscreen + + window.fullscreen = false + doAssert not window.fullscreen + + echo "SUCESS!" + quit() diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index 8b138b4..c54124d 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -36,7 +36,6 @@ type closeRequested, closed: bool runeInputEnabled: bool - innerVisible: bool innerDecorated: bool innerFocused: bool @@ -61,14 +60,23 @@ var clipboardWindow: XWindow clipboardContent: string -proc atom[name: static string](): Atom = - var a {.global.}: Atom - if a == 0: - a = display.XInternAtom(name, 0) - a - -template atom(name: static string): Atom = - atom[name]() +proc initConstants(display: Display) = + xaNetWMState = display.XInternAtom("_NET_WM_STATE", 0) + xaNetWMStateMaximizedHorz = display.XInternAtom("_NET_WM_STATE_MAXIMIZED_HORZ", 0) + xaNetWMStateMaximizedVert = display.XInternAtom("_NET_WM_STATE_MAXIMIZED_VERT", 0) + xaWMState = display.XInternAtom("WM_STATE", 0) + xaNetWMStateHiden = display.XInternAtom("_NET_WM_STATE_HIDDEN", 0) + xaNetWMStateFullscreen = display.XInternAtom("_NET_WM_STATE_FULLSCREEN", 0) + xaNetWMName = display.XInternAtom("_NET_WM_NAME", 0) + xaUTF8String = display.XInternAtom("UTF8_STRING", 0) + xaNetWMIconName = display.XInternAtom("_NET_WM_ICON_NAME", 0) + xaWMDeleteWindow = display.XInternAtom("WM_DELETE_WINDOW", 0) + xaNetWMSyncRequest = display.XInternAtom("_NET_WM_SYNC_REQUEST", 0) + xaNetWMSyncRequestCounter = display.XInternAtom("_NET_WM_SYNC_REQUEST_COUNTER", 0) + xaClipboard = display.XInternAtom("CLIPBOARD", 0) + xaWindyClipboardTargetProperty = display.XInternAtom("windy_clipboardTargetProperty", 0) + xaTargets = display.XInternAtom("TARGETS", 0) + xaText = display.XInternAtom("TEXT", 0) proc atomIfExist(name: string): Atom = display.XInternAtom(name, 1) @@ -87,6 +95,8 @@ proc init = if display == nil: raise WindyError.newException("Error opening X11 display, make sure the DISPLAY environment variable is set correctly") + display.initConstants() + wmForDecoratedKind = if (decoratedAtom = atomIfExist"_MOTIF_WM_HINTS"; decoratedAtom != 0): WmForDecoratedKind.motiv @@ -181,12 +191,12 @@ proc newClientMessage[T]( result.client.sendEvent = sendEvent proc wmState(window: XWindow): HashSet[Atom] = - window.property(atom"_NET_WM_STATE").data.asSeq(Atom).toHashSet + window.property(xaNetWMState).data.asSeq(Atom).toHashSet proc wmStateSend(window: XWindow, op: int, atom: Atom) = # op: 2 - switch, 1 - set true, 0 - set false display.defaultRootWindow.send( - window.newClientMessage(atom"_NET_WM_STATE", [Atom op, atom]), + window.newClientMessage(xaNetWMState, [Atom op, atom]), SubstructureNotifyMask or SubstructureRedirectMask ) @@ -301,7 +311,7 @@ proc keysymToButton(sym: KeySym): Button = proc queryKeyboardState(): set[0..255] = var r: array[32, char] display.XQueryKeymap(r) - result = cast[ptr set[0..255]](r.addr)[] + return cast[ptr set[0..255]](r.addr)[] proc destroy(window: Window) = if window.ic != nil: @@ -329,7 +339,10 @@ proc swapBuffers*(window: Window) = display.glXSwapBuffers(window.handle) proc visible*(window: Window): bool = - window.innerVisible + var + xwa: XWindowAttributes + display.XGetWindowAttributes(window.handle, xwa.addr) + return xwa.map_state == IsViewable proc `visible=`*(window: Window, v: bool) = if v: @@ -341,10 +354,21 @@ proc size*(window: Window): IVec2 = var xwa: XWindowAttributes display.XGetWindowAttributes(window.handle, xwa.addr) - result = xwa.size + return xwa.size proc `size=`*(window: Window, v: IVec2) = display.XResizeWindow(window.handle, v.x.uint32, v.y.uint32) + # Block until the size is correct + for i in 0 .. 100: + var + xwa: XWindowAttributes + display.XGetWindowAttributes(window.handle, xwa.addr) + if v == xwa.size: + break + if i == 100: + raise newException(WindyError, "Error setting window size.") + # Needs to swap buffer or viewport stays in incomplete state. + window.swapBuffers() proc framebufferSize*(window: Window): IVec2 = window.size @@ -370,17 +394,17 @@ proc `pos=`*(window: Window, v: IVec2) = proc maximized*(window: Window): bool = let wmState = window.handle.wmState - atom"_NET_WM_STATE_MAXIMIZED_HORZ" in wmState and - atom"_NET_WM_STATE_MAXIMIZED_VERT" in wmState + return xaNetWMStateMaximizedHorz in wmState and + xaNetWMStateMaximizedVert in wmState proc `maximized=`*(window: Window, v: bool) = - window.handle.wmStateSend v.int, atom"_NET_WM_STATE_MAXIMIZED_HORZ" - window.handle.wmStateSend v.int, atom"_NET_WM_STATE_MAXIMIZED_VERT" + window.handle.wmStateSend(v.int, xaNetWMStateMaximizedHorz) + window.handle.wmStateSend(v.int, xaNetWMStateMaximizedVert) proc minimized*(window: Window): bool = - let wState = window.handle.property(atom"WM_STATE").data.asSeq(int32) - wState.len >= 1 and wState[0] == 3 or - atom"_NET_WM_STATE_HIDDEN" in window.handle.wmState + let wState = window.handle.property(xaWMState).data.asSeq(int32) + return wState.len >= 1 and wState[0] == 3 or + xaNetWMStateHiden in window.handle.wmState proc `minimized=`*(window: Window, v: bool) = if v: @@ -395,10 +419,10 @@ proc focus*(window: Window) = display.XSetInputFocus(window.handle, rtNone) proc fullscreen*(window: Window): bool = - atom"_NET_WM_STATE_FULLSCREEN" in window.handle.wmState + xaNetWMStateFullscreen in window.handle.wmState proc `fullscreen=`*(window: Window, v: bool) = - window.handle.wmStateSend v.int, atom"_NET_WM_STATE_FULLSCREEN" + window.handle.wmStateSend(v.int, xaNetWMStateFullscreen) proc style*(window: Window): WindowStyle = if window.innerDecorated: @@ -434,7 +458,8 @@ proc `style=`*(window: Window, v: WindowStyle) = display.XMapWindow(window.handle) window.size = size # restore window size - else: discard + else: + discard case v of WindowStyle.Undecorated: @@ -486,11 +511,11 @@ proc `style=`*(window: Window, v: WindowStyle) = display.XSetNormalHints(window.handle, hints.addr) proc title*(window: Window): string = - window.handle.property(atom"_NET_WM_NAME").data + window.handle.property(xaNetWMName).data proc `title=`*(window: Window, v: string) = - window.handle.setProperty(atom"_NET_WM_NAME", atom"UTF8_STRING", 8, v) - window.handle.setProperty(atom"_NET_WM_ICON_NAME", atom"UTF8_STRING", 8, v) + window.handle.setProperty(xaNetWMName, xaUTF8String, 8, v) + window.handle.setProperty(xaNetWMIconName, xaUTF8String, 8, v) display.Xutf8SetWMProperties(window.handle, v, v, nil, 0, nil, nil, nil) proc contentScale*(window: Window): float32 = @@ -547,7 +572,7 @@ proc newWindow*( vi.depth.cuint, InputOutput, vi.visual, - CwColormap or CwEventMask or CwBorderPixel or CwBackPixel, + CwColormap or CwEventMask or CwBorderPixel, swa.addr ) @@ -565,7 +590,7 @@ proc newWindow*( FocusChangeMask ) - var wmProtocols = [atom"WM_DELETE_WINDOW", atom"_NET_WM_SYNC_REQUEST"] + var wmProtocols = [xaWMDeleteWindow, xaNetWMSyncRequest] display.XSetWMProtocols( result.handle, wmProtocols[0].addr, cint wmProtocols.len ) @@ -613,7 +638,7 @@ proc newWindow*( display.XSyncInitialize(vMaj.addr, vMin.addr) result.xsyncConter = display.XSyncCreateCounter(XSyncValue()) result.handle.setProperty( - atom"_NET_WM_SYNC_REQUEST_COUNTER", + xaNetWMSyncRequestCounter, xaCardinal, 32, @[result.xsyncConter].asString @@ -661,13 +686,13 @@ proc pollEvents(window: Window) = case ev.kind of xeClientMessage: - if ev.client.data.l[0] == "WM_DELETE_WINDOW".atom.clong: + if ev.client.data.l[0] == xaWMDeleteWindow.clong: window.closeRequested = true if window.onCloseRequest != nil: window.onCloseRequest() return # end polling events immediently - elif ev.client.data.l[0] == "_NET_WM_SYNC_REQUEST".atom.clong: + elif ev.client.data.l[0] == xaNetWMSyncRequest.clong: window.lastSync = XSyncValue( lo: cast[uint32](ev.client.data.l[2]), hi: cast[int32](ev.client.data.l[3]) @@ -690,7 +715,7 @@ proc pollEvents(window: Window) = pushButtonEvent(k, true) if window.onFocusChange != nil: - window.onFocusChange() + window.onFocusChange() of xeFocusOut: if not window.innerFocused: @@ -703,16 +728,12 @@ proc pollEvents(window: Window) = # release currently pressed keys let bd = window.buttonDown window.buttonDown = {} - for k in bd: pushButtonEvent(k.Button, false) + for k in bd: + pushButtonEvent(k.Button, false) if window.onFocusChange != nil: window.onFocusChange() - of xeMap: - window.innerVisible = true - of xeUnmap: - window.innerVisible = false - of xeConfigure: let pos = window.pos if pos != window.prevPos: @@ -767,10 +788,10 @@ proc pollEvents(window: Window) = of 8: pushButtonEvent(MouseButton4) of 9: pushButtonEvent(MouseButton5) - of 4: pushScrollEvent(vec2(1, 0)) # scroll up - of 5: pushScrollEvent(vec2(-1, 0)) # scroll down - of 6: pushScrollEvent(vec2(0, 1)) # scroll left? - of 7: pushScrollEvent(vec2(0, -1)) # scroll right? + of 4: pushScrollEvent(vec2(0, 1)) # scroll up + of 5: pushScrollEvent(vec2(0, -1)) # scroll down + of 6: pushScrollEvent(vec2(-1, 0)) # scroll left? + of 7: pushScrollEvent(vec2(1, 0)) # scroll right? else: discard if not isDblclk: @@ -800,8 +821,8 @@ proc pollEvents(window: Window) = for rune in s.runes: if window.onRune != nil: window.onRune(rune) - - else: discard + else: + discard proc mousePos*(window: Window): IVec2 = window.mousePos @@ -860,13 +881,13 @@ proc processClipboardEvents: bool = of xeSelection: template e: untyped = ev.selection - if e.property == 0 or e.selection != atom"CLIPBOARD": + if e.property == 0 or e.selection != xaClipboard: continue clipboardContent = clipboardWindow.property( - atom"windy_clipboardTargetProperty" + xaWindyClipboardTargetProperty ).data - clipboardWindow.delProperty(atom"windy_clipboardTargetProperty") + clipboardWindow.delProperty(xaWindyClipboardTargetProperty) return true @@ -882,19 +903,19 @@ proc processClipboardEvents: bool = time: e.time ) - if e.selection == atom"CLIPBOARD": - if e.target == atom"TARGETS": + if e.selection == xaClipboard: + if e.target == xaTargets: # request requests that we can handle e.requestor.setProperty(e.property, xaAtom, 32, @[ - atom"TARGETS", - atom"TEXT", + xaTargets, + xaText, xaString, - atom"UTF8_STRING" + xaUTF8String ].asString) e.requestor.send(XEvent(selection: resp), propagate = true) continue - elif e.target in {xaString, atom"TEXT", atom"UTF8_STRING"}: + elif e.target in {xaString, xaText, xaUTF8String}: # request clipboard data e.requestor.setProperty(e.property, xaAtom, 8, clipboardContent) e.requestor.send(XEvent(selection: resp), propagate = true) @@ -904,20 +925,21 @@ proc processClipboardEvents: bool = resp.property = 0 e.requestor.send(XEvent(selection: resp), propagate = true) - else: discard + else: + discard proc getClipboardString*: string = initClipboard() - if display.XGetSelectionOwner(atom"CLIPBOARD") == 0: + if display.XGetSelectionOwner(xaClipboard) == 0: return "" - if display.XGetSelectionOwner(atom"CLIPBOARD") == clipboardWindow: + if display.XGetSelectionOwner(xaClipboard) == clipboardWindow: return clipboardContent display.XConvertSelection( - atom"CLIPBOARD", - atom"UTF8_STRING", - atom"windy_clipboardTargetProperty", + xaClipboard, + xaUTF8String, + xaWindyClipboardTargetProperty, clipboardWindow ) @@ -928,7 +950,7 @@ proc getClipboardString*: string = proc setClipboardString*(s: string) = initClipboard() clipboardContent = s - display.XSetSelectionOwner(atom"CLIPBOARD", clipboardWindow) + display.XSetSelectionOwner(xaClipboard, clipboardWindow) proc pollEvents* = let ws = windows diff --git a/src/windy/platforms/linux/x11/x.nim b/src/windy/platforms/linux/x11/x.nim index b7d3522..93be845 100644 --- a/src/windy/platforms/linux/x11/x.nim +++ b/src/windy/platforms/linux/x11/x.nim @@ -409,3 +409,23 @@ const xaWmClass* = Atom 67 xaWmTransientFor* = Atom 68 xaLastPredefined* = Atom 68 + +var + # These "constants" are not known at compile time, + # but are poluatinged by x11 via initConstants(). + xaNetWMState*: Atom + xaNetWMStateMaximizedHorz*: Atom + xaNetWMStateMaximizedVert*: Atom + xaWMState*: Atom + xaNetWMStateHiden*: Atom + xaNetWMStateFullscreen*: Atom + xaNetWMName*: Atom + xaUTF8String*: Atom + xaNetWMIconName*: Atom + xaWMDeleteWindow*: Atom + xaNetWMSyncRequest*: Atom + xaNetWMSyncRequestCounter*: Atom + xaClipboard*: Atom + xaWindyClipboardTargetProperty*: Atom + xaTargets*: Atom + xaText*: Atom diff --git a/src/windy/platforms/win32/platform.nim b/src/windy/platforms/win32/platform.nim index 0decda6..ae2d04c 100644 --- a/src/windy/platforms/win32/platform.nim +++ b/src/windy/platforms/win32/platform.nim @@ -379,7 +379,7 @@ proc `pos=`*(window: Window, pos: IVec2) = if window.fullscreen: return - var rect = RECT(top: pos.x, left: pos.y, bottom: pos.x, right: pos.y) + var rect = RECT(top: pos.y, left: pos.x, bottom: pos.y, right: pos.x) discard AdjustWindowRectExForDpi( rect.addr, getWindowStyle(window.hWnd),