Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion siwin.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ feature "dev":
requires "sdl2"
requires "https://github.com/planetis-m/vulkan#b223dc9"


when fileExists("src/siwin/build_utils/tasks.nim"):
include "src/siwin/build_utils/tasks.nim"

Expand Down
31 changes: 31 additions & 0 deletions src/siwin/platforms/any/window.nim
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,37 @@ type
constraintAdjustment*: set[PopupConstraintAdjustment]
reactive*: bool

LayerSurfaceLayer* {.siwin_enum.} = enum
lslBackground
lslBottom
lslTop
lslOverlay

LayerSurfaceAnchor* {.siwin_enum.} = enum
lsaTop
lsaBottom
lsaLeft
lsaRight

LayerSurfaceKeyboardMode* {.siwin_enum.} = enum
lskNone
lskExclusive
lskOnDemand

LayerSurfaceMargins* = object
top*: int32
right*: int32
bottom*: int32
left*: int32

LayerSurfaceConfig* = object
layer*: LayerSurfaceLayer
anchors*: set[LayerSurfaceAnchor]
margins*: LayerSurfaceMargins
exclusiveZone*: int32
keyboardMode*: LayerSurfaceKeyboardMode
namespace*: string

WindowVisualCapability* {.siwin_enum.} = enum
wvcBackdropBlur
wvcBackdropBlurRegion
Expand Down
17 changes: 17 additions & 0 deletions src/siwin/platforms/wayland/siwinGlobals.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import ./[libwayland, protocol, bitfields, libdecor]
type
WaylandExtensionNotFound* = object of CatchableError

WaylandOutput* = object
registryName*: uint32
output*: Wl_output

SiwinGlobalsWayland* = ref SiwinGlobalsWaylandObj
SiwinGlobalsWaylandObj* = object of SiwinGlobals
seatEventsInitialized*: bool
seatCapabilitiesChanged*: proc(globals: SiwinGlobalsWayland)
dataDeviceManagerEventsInitialized*: bool

display*: WlDisplay
Expand All @@ -26,6 +31,7 @@ type
viewporter*: Wp_viewporter
fractionalScaleManager*: Wp_fractional_scale_manager_v1
xdgToplevelIconManager*: Xdg_toplevel_icon_manager_v1
outputs*: seq[WaylandOutput]

serverDecorationManager*: Zxdg_decoration_manager_v1
plasmaShell*: Org_kde_plasma_shell
Expand Down Expand Up @@ -118,6 +124,8 @@ proc initRegistryCallbacks(globals: SiwinGlobalsWayland) =
globals.seatCapabilities = globals.seatCapabilities.typeof.default
globals.seat.onCapabilities:
globals.seatCapabilities = capabilities.asBitfield
if not globals.seatCapabilitiesChanged.isNil:
globals.seatCapabilitiesChanged(globals)

discard wl_display_roundtrip globals.display

Expand Down Expand Up @@ -155,6 +163,9 @@ proc initRegistryCallbacks(globals: SiwinGlobalsWayland) =
addRegistry Wp_cursor_shape_manager_v1:
globals.cursorShapeManager = binded

addRegistry Wl_output:
globals.outputs.add WaylandOutput(registryName: name, output: binded)


proc isWaylandAvailable*: bool =
proc isSocket(filename: string): bool =
Expand Down Expand Up @@ -208,6 +219,12 @@ proc newWaylandGlobals*(): SiwinGlobalsWayland =
if interfaceString == targetIface:
callback(globals.registry, name, version)

globals.registry.onGlobal_remove:
for idx in countdown(globals.outputs.high, 0):
if globals.outputs[idx].registryName == name:
release globals.outputs[idx].output
globals.outputs.delete(idx)


proc roundtrip*(globals: SiwinGlobalsWayland) =
discard wl_display_roundtrip globals.display
Expand Down
119 changes: 106 additions & 13 deletions src/siwin/platforms/wayland/window.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ privateAccess Window
type
ScreenWayland* = ref object of Screen
id: cint
output: Wl_output

WindowWaylandKind* {.pure.} = enum
XdgSurface
Expand All @@ -22,6 +23,7 @@ type
WindowWayland* = ref WindowWaylandObj
WindowWaylandObj* = object of Window
globals: SiwinGlobalsWayland
screen: ScreenWayland
surface: Wl_surface
xdgSurface: Xdg_surface
xdgToplevel: Xdg_toplevel
Expand Down Expand Up @@ -63,6 +65,7 @@ type
lastTextEntered: string
lastPressedKeyTime: Time
lastKeyRepeatedTime: Time
initialConfigureReceived: bool
bufferScaleFactor: int32
fractionalScaleFactor: float32
popupRepositionToken: uint32
Expand Down Expand Up @@ -247,16 +250,19 @@ proc refreshKeyboardModifiers(window: WindowWayland) =
method swapBuffers(window: WindowWayland) {.base.} = discard

proc screenCountWayland*(globals: SiwinGlobalsWayland): int32 =
## todo
1
globals.outputs.len.int32

proc screenWayland*(globals: SiwinGlobalsWayland, number: int32): ScreenWayland =
new result
if number notin 0..<globals.screenCountWayland(): raise IndexDefect.newException(&"screen {number} doesn't exist")
result.id = number.cint
result.output = globals.outputs[number].output

proc defaultScreenWayland*(globals: SiwinGlobalsWayland): ScreenWayland =
ScreenWayland(id: 0.cint)
if globals.outputs.len == 0:
ScreenWayland(id: -1.cint)
else:
ScreenWayland(id: 0.cint, output: globals.outputs[0].output)

method number*(screen: ScreenWayland): int32 = screen.id

Expand Down Expand Up @@ -520,6 +526,7 @@ proc initClipboardsIfNeeded(globals: SiwinGlobalsWayland) =

proc basicInitWindow(window: WindowWayland; size: IVec2; screen: ScreenWayland) =
window.m_size = size
window.screen = screen
window.m_focused = false
window.m_resizable = true
window.m_frameless = true
Expand Down Expand Up @@ -669,6 +676,7 @@ proc createLibdecorFrameIface(): LibdecorFrameInterface =
LibdecorFrameInterface(
configure: proc(frame: LibdecorFrame, cfg: LibdecorConfiguration, userData: pointer) {.cdecl.} =
let win = cast[WindowWayland](userData)
win.initialConfigureReceived = true
var w, h: cint
if libdecor_configuration_get_content_size(cfg, frame, w.addr, h.addr):
if w > 0 and h > 0:
Expand Down Expand Up @@ -1157,12 +1165,15 @@ var cursor: Wp_cursor_shape_device_v1
var cursor_surface: CursorWayland

proc initSeatEvents*(globals: SiwinGlobalsWayland) =
if globals.seatEventsInitialized: return
globals.seatEventsInitialized = true
if not globals.seatEventsInitialized:
globals.seatEventsInitialized = true
globals.seatCapabilitiesChanged = proc(updatedGlobals: SiwinGlobalsWayland) =
updatedGlobals.initSeatEvents()

if globals.seat == nil: return

if `WlSeat / Capability`.`pointer` in globals.seatCapabilities:
if `WlSeat / Capability`.`pointer` in globals.seatCapabilities and
globals.seat_pointer == nil:
globals.seat_pointer = globals.seat.get_pointer

cursor_surface = globals.loadBuiltinCursor(arrow)
Expand Down Expand Up @@ -1302,7 +1313,8 @@ proc initSeatEvents*(globals: SiwinGlobalsWayland) =
return


if `WlSeat / Capability`.keyboard in globals.seatCapabilities:
if `WlSeat / Capability`.keyboard in globals.seatCapabilities and
globals.seat_keyboard == nil:
globals.seat_keyboard = globals.seat.get_keyboard
# Default repeat values; compositor-provided repeat_info overrides these.
globals.seat_keyboard_repeatSettings = (rate: 25, delay: 600)
Expand Down Expand Up @@ -1409,7 +1421,7 @@ proc initSeatEvents*(globals: SiwinGlobalsWayland) =
globals.seat_keyboard_repeatSettings = (rate: rate, delay: delay)


if globals.tabletManager != nil:
if globals.tabletManager != nil and globals.seat_tablet == nil:
globals.seat_tablet = globals.tabletManager.get_tablet_seat(globals.seat)

globals.seat_tablet.onTool_added:
Expand Down Expand Up @@ -1727,6 +1739,7 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
window.xdgToplevel = window.xdgSurface.get_toplevel

window.xdgSurface.onConfigure:
window.initialConfigureReceived = true
window.xdgSurface.ack_configure(serial)
redraw window

Expand Down Expand Up @@ -1774,6 +1787,7 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
destroy positioner

window.xdgSurface.onConfigure:
window.initialConfigureReceived = true
window.xdgSurface.ack_configure(serial)
redraw window

Expand All @@ -1792,16 +1806,18 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
window.resize(ivec2(width, height))

of LayerSurface:
expectExtension window.globals.layerShell
window.layerShellSurface = window.globals.layerShell.get_layer_surface(
window.surface,
Wl_output(proxy: Wl_proxy(raw: nil)),
window.screen.output,
`Zwlr_layer_shell_v1/Layer`(window.layer.int),
window.namespace.cstring
)
window.layerShellSurface.set_size(window.m_size.x.uint32, window.m_size.y.uint32)
window.redraw()

window.layerShellSurface.onConfigure:
window.initialConfigureReceived = true
window.layerShellSurface.ack_configure(serial)
window.resize(ivec2(width.int32, height.int32))

Expand Down Expand Up @@ -1898,6 +1914,19 @@ proc setExclusiveZone*(window: WindowWayland, zone: int32) =
window.layerShellSurface.set_exclusive_zone(zone)


proc setMargins*(window: WindowWayland, margins: LayerSurfaceMargins) =
if window.layerShellSurface == nil:
raise newException(
ValueError,
"Attempt to set margins when layer shell surface hasn't been initialized." &
"\nHint: Construct this window with newLayerSurfaceWindowWayland()."
)

window.layerShellSurface.set_margin(
margins.top, margins.right, margins.bottom, margins.left
)


proc constructClipboardContent*(
data: sink string, kind: ClipboardContentKind, mimeType: string
): ClipboardContent =
Expand Down Expand Up @@ -2111,17 +2140,32 @@ method `content=`*(clipboard: ClipboardWayland, content: ClipboardConvertableCon
discard wl_display_roundtrip clipboard.globals.display


proc configureSurface*(window: WindowWayland) =
## Commit the role setup and wait for the compositor's initial configure.
##
## An xdg_surface must acknowledge its initial configure before a renderer
## attaches its first buffer. Vulkan renderers may create and present a
## swapchain before the caller reaches `firstStep`, so Vulkan window setup
## calls this before exposing the surface.
window.surface.commit()
while true:
if window.globals.libdecorCtx != nil:
discard libdecor_dispatch(window.globals.libdecorCtx, 0)
let eventCount = wl_display_roundtrip(window.globals.display)
if eventCount < 0:
raise newException(RoundtripFailed, "wl_display_roundtrip() returned " & $eventCount)
if window.initialConfigureReceived:
break


method firstStep*(window: WindowWayland, makeVisible = true) =
if makeVisible:
window.visible = true

# window.m_pos = window.handle.geometry.pos
# window.mouse.pos = cursor().pos - window.m_pos

window.surface.commit()
if window.globals.libdecorCtx != nil:
discard libdecor_dispatch(window.globals.libdecorCtx, 0)
discard wl_display_roundtrip window.globals.display
window.configureSurface()

if window.opened: window.eventsHandler.onResize.pushEvent ResizeEvent(window: window, size: window.size, initial: true)
window.lastTickTime = getTime()
Expand Down Expand Up @@ -2227,6 +2271,55 @@ proc newSoftwareRenderingWindowWayland*(
result.title = title
if not resizable: result.resizable = false

proc newLayerSurfaceWindowWayland*(
globals: SiwinGlobalsWayland,
size = ivec2(1280, 32),
title = "",
screen: ScreenWayland,
config: LayerSurfaceConfig,
transparent = false,
): WindowWaylandSoftwareRendering =
new result
result.globals = globals
result.softwarePresentEnabled = true
result.kind = WindowWaylandKind.LayerSurface
result.layer = Layer(config.layer.ord)
result.namespace = config.namespace
result.basicInitWindow(size, screen)
result.setupWindow(
fullscreen = false,
frameless = true,
transparent = transparent,
size = size,
class = title,
)

var anchors: uint32
for anchor in config.anchors:
anchors = anchors or (1'u32 shl anchor.ord)
result.layerShellSurface.set_anchor(
cast[`Zwlr_layer_surface_v1/Anchor`](anchors)
)
result.setMargins(config.margins)
result.setExclusiveZone(config.exclusiveZone)
result.setKeyboardInteractivity(
LayerInteractivityMode(config.keyboardMode.ord)
)

let
requestedWidth =
if {lsaLeft, lsaRight} <= config.anchors: 0'u32 else: size.x.uint32
requestedHeight =
if {lsaTop, lsaBottom} <= config.anchors: 0'u32 else: size.y.uint32
result.layerShellSurface.set_size(requestedWidth, requestedHeight)
result.buffer = result.globals.create(
result.globals.shm,
result.bufferSize(size),
(if transparent: argb8888 else: xrgb8888),
bufferCount = 2,
)
result.title = title

proc newPopupWindowWayland*(
globals: SiwinGlobalsWayland,
parent: WindowWayland,
Expand Down
1 change: 1 addition & 0 deletions src/siwin/platforms/wayland/windowVulkan.nim
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ proc initVulkanWindow(
window.basicInitWindow size, screen

window.setupWindow fullscreen, frameless, transparent, size, class
window.configureSurface()

# window.eglContext = newOpenglContext(window.surface.proxy.raw, size.x, size.y)
# makeCurrent window.eglContext
Expand Down
31 changes: 31 additions & 0 deletions src/siwin/window.nim
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,37 @@ when not siwin_use_lib:
elif defined(macosx):
result = newPopupWindowCocoa(parent.WindowCocoa, placement, transparent, grab)

proc newLayerSurfaceWindow*(
globals: SiwinGlobals,
size = ivec2(1280, 32),
title = "",
screen: int32 = -1,
config: LayerSurfaceConfig,
transparent = false,
): Window =
when defined(linux) or defined(bsd):
if globals of SiwinGlobalsWayland:
let waylandGlobals = globals.SiwinGlobalsWayland
result = waylandGlobals.newLayerSurfaceWindowWayland(
size = size,
title = title,
screen =
if screen == -1:
waylandGlobals.defaultScreenWayland()
else:
waylandGlobals.screenWayland(screen),
config = config,
transparent = transparent,
)
else:
raise SiwinPlatformSupportDefect.newException(
"Layer-shell surfaces require the Wayland platform"
)
else:
raise SiwinPlatformSupportDefect.newException(
"Layer-shell surfaces require Wayland"
)


when defined(android):
proc loadExtensions*() =
Expand Down
Loading