@@ -64,6 +64,7 @@ extern const struct wl_registry_listener gio_registry_listener;
6464extern const struct wl_surface_listener gio_surface_listener;
6565extern const struct xdg_surface_listener gio_xdg_surface_listener;
6666extern const struct xdg_toplevel_listener gio_xdg_toplevel_listener;
67+ extern const struct zxdg_toplevel_decoration_v1_listener gio_zxdg_toplevel_decoration_v1_listener;
6768extern const struct xdg_wm_base_listener gio_xdg_wm_base_listener;
6869extern const struct wl_callback_listener gio_callback_listener;
6970extern const struct wl_output_listener gio_output_listener;
@@ -149,6 +150,7 @@ type repeatState struct {
149150type window struct {
150151 w * callbacks
151152 disp * wlDisplay
153+ seat * wlSeat
152154 surf * C.struct_wl_surface
153155 wmSurf * C.struct_xdg_surface
154156 topLvl * C.struct_xdg_toplevel
@@ -188,9 +190,10 @@ type window struct {
188190 newScale bool
189191 scale int
190192 // size is the unscaled window size (unlike config.Size which is scaled).
191- size image.Point
192- config Config
193- wsize image.Point // window config size before going fullscreen
193+ size image.Point
194+ config Config
195+ wsize image.Point // window config size before going fullscreen or maximized
196+ inCompositor bool // window is moving or being resized
194197
195198 wakeups chan struct {}
196199}
@@ -212,7 +215,7 @@ type wlOutput struct {
212215}
213216
214217// callbackMap maps Wayland native handles to corresponding Go
215- // references. It is necessary because the the Wayland client API
218+ // references. It is necessary because the Wayland client API
216219// forces the use of callbacks and storing pointers to Go values
217220// in C is forbidden.
218221var callbackMap sync.Map
@@ -369,9 +372,8 @@ func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) {
369372 C .xdg_toplevel_add_listener (w .topLvl , & C .gio_xdg_toplevel_listener , unsafe .Pointer (w .surf ))
370373
371374 if d .decor != nil {
372- // Request server side decorations.
373375 w .decor = C .zxdg_decoration_manager_v1_get_toplevel_decoration (d .decor , w .topLvl )
374- C .zxdg_toplevel_decoration_v1_set_mode (w .decor , C . ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE )
376+ C .zxdg_toplevel_decoration_v1_add_listener (w .decor , & C . gio_zxdg_toplevel_decoration_v1_listener , unsafe . Pointer ( w . surf ) )
375377 }
376378 w .updateOpaqueRegion ()
377379 return w , nil
@@ -499,6 +501,24 @@ func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel,
499501 w .size = image .Pt (int (width ), int (height ))
500502 w .updateOpaqueRegion ()
501503 }
504+ w .needAck = true
505+ }
506+
507+ //export gio_onToplevelDecorationConfigure
508+ func gio_onToplevelDecorationConfigure (data unsafe.Pointer , deco * C.struct_zxdg_toplevel_decoration_v1 , mode C.uint32_t ) {
509+ w := callbackLoad (data ).(* window )
510+ decorated := w .config .Decorated
511+ switch mode {
512+ case C .ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
513+ w .config .Decorated = false
514+ case C .ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE :
515+ w .config .Decorated = true
516+ }
517+ if decorated != w .config .Decorated {
518+ w .w .Event (ConfigEvent {Config : w .config })
519+ }
520+ w .needAck = true
521+ w .draw (true )
502522}
503523
504524//export gio_onOutputMode
@@ -772,15 +792,22 @@ func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, seria
772792 s := callbackLoad (data ).(* wlSeat )
773793 s .serial = serial
774794 w := callbackLoad (unsafe .Pointer (surf )).(* window )
795+ w .seat = s
775796 s .pointerFocus = w
776797 w .setCursor (pointer , serial )
777798 w .lastPos = f32.Point {X : fromFixed (x ), Y : fromFixed (y )}
778799}
779800
780801//export gio_onPointerLeave
781- func gio_onPointerLeave (data unsafe.Pointer , p * C.struct_wl_pointer , serial C.uint32_t , surface * C.struct_wl_surface ) {
802+ func gio_onPointerLeave (data unsafe.Pointer , p * C.struct_wl_pointer , serial C.uint32_t , surf * C.struct_wl_surface ) {
803+ w := callbackLoad (unsafe .Pointer (surf )).(* window )
804+ w .seat = nil
782805 s := callbackLoad (data ).(* wlSeat )
783806 s .serial = serial
807+ if w .inCompositor {
808+ w .inCompositor = false
809+ w .w .Event (pointer.Event {Type : pointer .Cancel })
810+ }
784811}
785812
786813//export gio_onPointerMotion
@@ -818,6 +845,8 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
818845 case 0 :
819846 w .pointerBtns &^= btn
820847 typ = pointer .Release
848+ // Move or resize gestures no longer applies.
849+ w .inCompositor = false
821850 case 1 :
822851 w .pointerBtns |= btn
823852 typ = pointer .Press
@@ -978,6 +1007,9 @@ func (w *window) Configure(options []Option) {
9781007 C .xdg_toplevel_set_max_size (w .topLvl , C .int32_t (cnf .MaxSize .X ), C .int32_t (cnf .MaxSize .Y ))
9791008 }
9801009 }
1010+ if cnf .Decorated != prev .Decorated {
1011+ w .config .Decorated = cnf .Decorated
1012+ }
9811013 if w .config != prev {
9821014 w .w .Event (ConfigEvent {Config : w .config })
9831015 }
@@ -992,6 +1024,63 @@ func (w *window) setTitle(prev, cnf Config) {
9921024 }
9931025}
9941026
1027+ func (w * window ) Perform (actions system.Action ) {
1028+ walkActions (actions , func (action system.Action ) {
1029+ switch action {
1030+ case system .ActionMinimize :
1031+ w .Configure ([]Option {Minimized .Option ()})
1032+ case system .ActionMaximize :
1033+ w .Configure ([]Option {Maximized .Option ()})
1034+ case system .ActionUnmaximize :
1035+ w .Configure ([]Option {Windowed .Option ()})
1036+ case system .ActionClose :
1037+ w .Close ()
1038+ case system .ActionMove :
1039+ w .move ()
1040+ default :
1041+ w .resize (action )
1042+ }
1043+ })
1044+ }
1045+
1046+ func (w * window ) move () {
1047+ if ! w .inCompositor && w .seat != nil {
1048+ w .inCompositor = true
1049+ s := w .seat
1050+ C .xdg_toplevel_move (w .topLvl , s .seat , s .serial )
1051+ }
1052+ }
1053+
1054+ func (w * window ) resize (a system.Action ) {
1055+ if w .inCompositor || w .seat == nil {
1056+ return
1057+ }
1058+ var edge int
1059+ switch a {
1060+ case system .ActionResizeNorth :
1061+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_TOP
1062+ case system .ActionResizeSouth :
1063+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM
1064+ case system .ActionResizeEast :
1065+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_LEFT
1066+ case system .ActionResizeWest :
1067+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_RIGHT
1068+ case system .ActionResizeNorthWest :
1069+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT
1070+ case system .ActionResizeNorthEast :
1071+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT
1072+ case system .ActionResizeSouthEast :
1073+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT
1074+ case system .ActionResizeSouthWest :
1075+ edge = C .XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT
1076+ default :
1077+ return
1078+ }
1079+ w .inCompositor = true
1080+ s := w .seat
1081+ C .xdg_toplevel_resize (w .topLvl , s .seat , s .serial , C .uint32_t (edge ))
1082+ }
1083+
9951084func (w * window ) Raise () {
9961085 // NB. there is no way for a minimized window to be unminimized.
9971086 // https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_minimized
0 commit comments