mirror of
https://github.com/gnustep/libs-back.git
synced 2025-02-24 04:11:28 +00:00
1090 lines
29 KiB
Objective-C
1090 lines
29 KiB
Objective-C
/*
|
|
WaylandServer - Wayland Server Class
|
|
|
|
Copyright (C) 2020 Free Software Foundation, Inc.
|
|
|
|
Author: Sergio L. Pascual <slp@sinrega.org>
|
|
Rewrite: Riccardo Canalicchio <riccardo.canalicchio(at)gmail.com>
|
|
Date: November 2021
|
|
|
|
This file is part of the GNU Objective C Backend Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; see the file COPYING.LIB.
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <AppKit/AppKitExceptions.h>
|
|
#include <AppKit/NSApplication.h>
|
|
#include <AppKit/NSGraphics.h>
|
|
#include <AppKit/DPSOperators.h>
|
|
#include <AppKit/NSEvent.h>
|
|
#include <AppKit/NSWindow.h>
|
|
#include <AppKit/NSMenu.h>
|
|
#include <Foundation/NSConnection.h>
|
|
#include <Foundation/NSRunLoop.h>
|
|
#include <Foundation/NSException.h>
|
|
#include <Foundation/NSDebug.h>
|
|
#include <Foundation/NSValue.h>
|
|
|
|
#include <unistd.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
#include <linux/input.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include "wayland/WaylandServer.h"
|
|
|
|
extern const struct wl_output_listener output_listener;
|
|
|
|
extern const struct wl_seat_listener seat_listener;
|
|
|
|
static void
|
|
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
|
{}
|
|
|
|
struct wl_shm_listener shm_listener = {shm_format};
|
|
|
|
extern const struct xdg_surface_listener xdg_surface_listener;
|
|
|
|
extern const struct xdg_toplevel_listener xdg_toplevel_listener;
|
|
|
|
extern const struct xdg_wm_base_listener wm_base_listener;
|
|
|
|
extern const struct zwlr_layer_surface_v1_listener layer_surface_listener;
|
|
|
|
extern const struct xdg_popup_listener xdg_popup_listener;
|
|
|
|
static void
|
|
handle_global(void *data, struct wl_registry *registry, uint32_t name,
|
|
const char *interface, uint32_t version)
|
|
{
|
|
WaylandConfig *wlconfig = data;
|
|
|
|
NSDebugLog(@"wayland: registering interface '%s'", interface);
|
|
if (strcmp(interface, xdg_wm_base_interface.name) == 0)
|
|
{
|
|
wlconfig->wm_base
|
|
= wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
xdg_wm_base_add_listener(wlconfig->wm_base, &wm_base_listener, NULL);
|
|
NSDebugLog(@"wayland: found wm_base interface");
|
|
}
|
|
else if (strcmp(interface, wl_shell_interface.name) == 0)
|
|
{
|
|
wlconfig->shell
|
|
= wl_registry_bind(registry, name, &wl_shell_interface, 1);
|
|
NSDebugLog(@"wayland: found shell interface");
|
|
}
|
|
else if (strcmp(interface, wl_compositor_interface.name) == 0)
|
|
{
|
|
wlconfig->compositor
|
|
= wl_registry_bind(registry, name, &wl_compositor_interface, 1);
|
|
NSDebugLog(@"wayland: found compositor interface");
|
|
}
|
|
else if (strcmp(interface, wl_shm_interface.name) == 0)
|
|
{
|
|
wlconfig->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
|
NSDebugLog(@"wayland: found shm interface");
|
|
wl_shm_add_listener(wlconfig->shm, &shm_listener, wlconfig);
|
|
}
|
|
else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0)
|
|
{
|
|
wlconfig->layer_shell
|
|
= wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
|
|
NSDebugLog(@"wayland: found wlr-layer_shell interface");
|
|
}
|
|
else if (strcmp(interface, wl_output_interface.name) == 0)
|
|
{
|
|
struct output *output = (struct output *) malloc(sizeof(struct output));
|
|
memset(output, 0, sizeof(struct output));
|
|
output->wlconfig = wlconfig;
|
|
output->scale = 1;
|
|
output->output
|
|
= wl_registry_bind(registry, name, &wl_output_interface, 2);
|
|
output->server_output_id = name;
|
|
NSDebugLog(@"wayland: found output interface");
|
|
wl_list_insert(wlconfig->output_list.prev, &output->link);
|
|
wlconfig->output_count++;
|
|
wl_output_add_listener(output->output, &output_listener, output);
|
|
}
|
|
else if (strcmp(interface, wl_seat_interface.name) == 0)
|
|
{
|
|
wlconfig->pointer.wlpointer = NULL;
|
|
wlconfig->seat_version = version;
|
|
wlconfig->seat
|
|
= wl_registry_bind(wlconfig->registry, name, &wl_seat_interface, 1);
|
|
NSDebugLog(@"wayland: found seat interface");
|
|
wl_seat_add_listener(wlconfig->seat, &seat_listener, wlconfig);
|
|
}
|
|
}
|
|
|
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
uint32_t name) {}
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
handle_global, handle_global_remove};
|
|
|
|
struct window *get_window_with_id(WaylandConfig *wlconfig, int winid) {
|
|
struct window *window;
|
|
|
|
wl_list_for_each(window, &wlconfig->window_list, link) {
|
|
if (window->window_id == winid) {
|
|
return window;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
float
|
|
WaylandToNS(struct window *window, float wl_y)
|
|
{
|
|
return (window->output->height - wl_y - window->height);
|
|
}
|
|
|
|
int
|
|
NSToWayland(struct window *window, int ns_y)
|
|
{
|
|
return (window->output->height - ns_y - window->height);
|
|
}
|
|
|
|
@class NSMenuPanel;
|
|
|
|
@implementation WaylandServer
|
|
|
|
/* Initialize AppKit backend */
|
|
+ (void)initializeBackend
|
|
{
|
|
NSDebugLog(@"Initializing GNUstep Wayland backend");
|
|
[GSDisplayServer setDefaultServerClass:[WaylandServer class]];
|
|
}
|
|
|
|
- (id)_initWaylandContext
|
|
{
|
|
wlconfig = (WaylandConfig *) malloc(sizeof(WaylandConfig));
|
|
memset(wlconfig, 0, sizeof(WaylandConfig));
|
|
wlconfig->last_window_id = 1;
|
|
wlconfig->mouse_scroll_multiplier = 1.0f;
|
|
wl_list_init(&wlconfig->output_list);
|
|
wl_list_init(&wlconfig->window_list);
|
|
|
|
wlconfig->display = wl_display_connect(NULL);
|
|
if (!wlconfig->display)
|
|
{
|
|
[NSException raise:NSWindowServerCommunicationException
|
|
format:@"Unable to connect Wayland Server"];
|
|
}
|
|
|
|
wlconfig->registry = wl_display_get_registry(wlconfig->display);
|
|
if (!wlconfig->registry)
|
|
{
|
|
[NSException raise:NSWindowServerCommunicationException
|
|
format:@"Unable to get global registry"];
|
|
}
|
|
wl_registry_add_listener(wlconfig->registry, ®istry_listener, wlconfig);
|
|
|
|
wl_display_dispatch(wlconfig->display);
|
|
wl_display_roundtrip(wlconfig->display);
|
|
|
|
if (!wlconfig->compositor)
|
|
{
|
|
[NSException raise:NSWindowServerCommunicationException
|
|
format:@"Unable to get compositor"];
|
|
}
|
|
if (!wlconfig->wm_base)
|
|
{
|
|
/* Note: this was merged into Weston only as of Feb 2019, and is
|
|
probably in Weston only as of 6.0 release, therefore not in Weston
|
|
5.x present in Debian buster (current stable). See Weston merge request
|
|
!103. */
|
|
[NSException
|
|
raise:NSWindowServerCommunicationException
|
|
format:@"Unable to get xdg-shell / xdg_wm_base - your Wayland "
|
|
@"compositor must support the stable XDG Shell protocol"];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)receivedEvent:(void *)data
|
|
type:(RunLoopEventType)type
|
|
extra:(void *)extra
|
|
forMode:(NSString *)mode
|
|
{
|
|
if (type == ET_RDESC)
|
|
{
|
|
// NSDebugLog(@"receivedEvent ET_RDESC");
|
|
if (wl_display_dispatch(wlconfig->display) == -1)
|
|
{
|
|
[NSException raise:NSWindowServerCommunicationException
|
|
format:@"Connection to Wayland Server lost"];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setupRunLoopInputSourcesForMode:(NSString *)mode
|
|
{
|
|
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
|
|
long fdWaylandHandle = wl_display_get_fd(wlconfig->display);
|
|
|
|
[currentRunLoop addEvent:(void *) fdWaylandHandle
|
|
type:ET_RDESC
|
|
watcher:(id<RunLoopEvents>) self
|
|
forMode:mode];
|
|
}
|
|
|
|
- (id)initWithAttributes:(NSDictionary *)info
|
|
{
|
|
NSDebugLog(@"WaylandServer initWithAttributes");
|
|
[super initWithAttributes:info];
|
|
[self _initWaylandContext];
|
|
|
|
[self setupRunLoopInputSourcesForMode:NSDefaultRunLoopMode];
|
|
[self setupRunLoopInputSourcesForMode:NSConnectionReplyMode];
|
|
[self setupRunLoopInputSourcesForMode:NSModalPanelRunLoopMode];
|
|
[self setupRunLoopInputSourcesForMode:NSEventTrackingRunLoopMode];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
NSDebugLog(@"Destroying Wayland Server");
|
|
[super dealloc];
|
|
}
|
|
|
|
- (BOOL)handlesWindowDecorations
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
- (void)restrictWindow:(int)win toImage:(NSImage *)image
|
|
{
|
|
NSDebugLog(@"restrictWindow");
|
|
}
|
|
|
|
- (NSRect)boundsForScreen:(int)screen
|
|
{
|
|
NSDebugLog(@"boundsForScreen: %d", screen);
|
|
struct output *output;
|
|
|
|
wl_list_for_each(output, &wlconfig->output_list, link)
|
|
{
|
|
NSDebugLog(@"screen found: %dx%d", output->width, output->height);
|
|
return NSMakeRect(0, 0, output->width, output->height);
|
|
}
|
|
|
|
NSDebugLog(@"can't find screen");
|
|
return NSZeroRect;
|
|
}
|
|
|
|
- (NSWindowDepth)windowDepthForScreen:(int)screen
|
|
{
|
|
NSDebugLog(@"windowDepthForScreen: %d", screen);
|
|
return (_GSRGBBitValue | 8);
|
|
}
|
|
|
|
- (const NSWindowDepth *)availableDepthsForScreen:(int)screen
|
|
{
|
|
NSDebugLog(@"availableDepthsForScreen");
|
|
return NULL;
|
|
}
|
|
|
|
- (NSArray *)screenList
|
|
{
|
|
NSDebugLog(@"screenList");
|
|
NSMutableArray *screens =
|
|
[NSMutableArray arrayWithCapacity:wlconfig->output_count];
|
|
struct output *output;
|
|
|
|
wl_list_for_each(output, &wlconfig->output_list, link)
|
|
{
|
|
[screens addObject:[NSNumber numberWithInt:output->server_output_id]];
|
|
NSDebugLog(@"adding screen with output_id=%d", output->server_output_id);
|
|
NSDebugLog(@"output dimensions: %dx%d %dx%d", output->alloc_x,
|
|
output->alloc_y, output->width, output->height);
|
|
}
|
|
|
|
return screens;
|
|
}
|
|
|
|
- (void *)serverDevice
|
|
{
|
|
NSDebugLog(@"serverDevice");
|
|
return NULL;
|
|
}
|
|
|
|
- (void *)windowDevice:(int)win
|
|
{
|
|
NSDebugLog(@"windowDevice");
|
|
return NULL;
|
|
}
|
|
|
|
- (void)beep
|
|
{
|
|
NSDebugLog(@"beep");
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation
|
|
WaylandServer (WindowOps)
|
|
|
|
- (int)window:(NSRect)
|
|
frame:(NSBackingStoreType)type
|
|
:(unsigned int)style
|
|
:(int)screen
|
|
{
|
|
NSDebugLog(@"window: screen=%d frame=%@", screen, NSStringFromRect(frame));
|
|
struct window *window;
|
|
struct output *output;
|
|
int width;
|
|
int height;
|
|
int altered = 0;
|
|
|
|
/* We're not allowed to create a zero rect window */
|
|
if (NSWidth(frame) <= 0 || NSHeight(frame) <= 0)
|
|
{
|
|
NSDebugLog(@"trying to create a zero rect window");
|
|
frame.size.width = 2;
|
|
frame.size.height = 2;
|
|
}
|
|
|
|
window = malloc(sizeof(struct window));
|
|
memset(window, 0, sizeof(struct window));
|
|
|
|
wl_list_for_each(output, &wlconfig->output_list, link)
|
|
{
|
|
if (output->server_output_id == screen)
|
|
{
|
|
window->output = output;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!window->output)
|
|
{
|
|
NSDebugLog(@"can't find screen %d", screen);
|
|
free(window);
|
|
return 0;
|
|
}
|
|
|
|
window->wlconfig = wlconfig;
|
|
window->instance = self;
|
|
window->is_out = 0;
|
|
window->width = width = NSWidth(frame);
|
|
window->height = height = NSHeight(frame);
|
|
window->pos_x = frame.origin.x;
|
|
window->pos_y = NSToWayland(window, frame.origin.y);
|
|
window->window_id = wlconfig->last_window_id;
|
|
|
|
window->xdg_surface = NULL;
|
|
window->toplevel = NULL;
|
|
window->popup = NULL;
|
|
window->layer_surface = NULL;
|
|
window->configured = NO;
|
|
|
|
window->buffer_needs_attach = NO;
|
|
window->terminated = NO;
|
|
|
|
window->moving = NO;
|
|
window->resizing = NO;
|
|
|
|
// FIXME is this needed?
|
|
if (window->pos_x < 0)
|
|
{
|
|
window->pos_x = 0;
|
|
altered = 1;
|
|
}
|
|
|
|
NSDebugLog(@"creating new window with id=%d: pos=%fx%f, size=%fx%f",
|
|
window->window_id, window->pos_x, window->pos_y, window->width,
|
|
window->height);
|
|
|
|
wl_list_insert(wlconfig->window_list.prev, &window->link);
|
|
wlconfig->last_window_id++;
|
|
wlconfig->window_count++;
|
|
|
|
// creates a buffer for the window
|
|
[self _setWindowOwnedByServer:(int) window->window_id];
|
|
|
|
if (altered)
|
|
{
|
|
NSEvent *ev =
|
|
[NSEvent otherEventWithType:NSAppKitDefined
|
|
location:NSZeroPoint
|
|
modifierFlags:0
|
|
timestamp:0
|
|
windowNumber:(int) window->window_id
|
|
context:GSCurrentContext()
|
|
subtype:GSAppKitWindowMoved
|
|
data1:window->pos_x
|
|
data2:WaylandToNS(window, window->pos_y)];
|
|
[(GSWindowWithNumber(window->window_id)) sendEvent:ev];
|
|
NSDebugLog(@"window: notifying of move=%fx%f", window->pos_x,
|
|
WaylandToNS(window, window->pos_y));
|
|
}
|
|
|
|
return window->window_id;
|
|
}
|
|
|
|
- (void)termwindow:(int)win
|
|
{
|
|
NSDebugLog(@"termwindow: win=%d", win);
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
|
|
[self destroyWindowShell:window];
|
|
// FIXME should wait for buffer release before detroying it
|
|
//
|
|
// wl_buffer_destroy(window->buffer);
|
|
wl_list_remove(&window->link);
|
|
window->terminated = YES;
|
|
}
|
|
|
|
- (int)nativeWindow:(void *)
|
|
winref:(NSRect *)frame
|
|
:(NSBackingStoreType *)type
|
|
:(unsigned int *)style
|
|
:(int *)screen
|
|
{
|
|
NSDebugLog(@"nativeWindow");
|
|
return 0;
|
|
}
|
|
|
|
- (void)stylewindow:(unsigned int)style:(int)win
|
|
{
|
|
NSDebugLog(@"stylewindow");
|
|
}
|
|
|
|
- (void)windowbacking:(NSBackingStoreType)type:(int)win
|
|
{
|
|
NSDebugLog(@"windowbacking");
|
|
}
|
|
|
|
- (void)titlewindow:(NSString *)window_title:(int)win
|
|
{
|
|
NSDebugLog(@"titlewindow: win=%d title=%@", win, window_title);
|
|
if (window_title == @"Window")
|
|
{
|
|
return;
|
|
}
|
|
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
const char *cString = [window_title UTF8String];
|
|
|
|
if (window->toplevel)
|
|
{
|
|
xdg_toplevel_set_title(window->toplevel, cString);
|
|
}
|
|
}
|
|
|
|
- (void)miniwindow:(int)win
|
|
{
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
|
|
NSDebugLog(@"miniwindow");
|
|
if (window->toplevel)
|
|
{
|
|
xdg_toplevel_set_minimized(window->toplevel);
|
|
}
|
|
else
|
|
{
|
|
NSDebugLog(@"trying to miniaturize a not toplevel window");
|
|
}
|
|
}
|
|
|
|
- (void)setWindowdevice:(int)winId forContext:(NSGraphicsContext *)ctxt
|
|
{
|
|
NSDebugLog(@"[%d] setWindowdevice", winId);
|
|
struct window *window;
|
|
|
|
window = get_window_with_id(wlconfig, winId);
|
|
|
|
GSSetDevice(ctxt, window, 0.0, window->height);
|
|
DPSinitmatrix(ctxt);
|
|
DPSinitclip(ctxt);
|
|
}
|
|
|
|
- (void)orderwindow:(int)op:(int)otherWin:(int)win
|
|
{
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
|
|
if (op == NSWindowOut)
|
|
{
|
|
NSDebugLog(@"[%d] orderwindow: NSWindowOut", win);
|
|
[self destroyWindowShell:window];
|
|
}
|
|
else
|
|
{ // NSWindowAbove || NSWindowBelow,
|
|
|
|
// currently it only creates a new shell for the window which results
|
|
// in popping in front of the window manager
|
|
NSDebugLog(@"[%d] orderwindow to: %fx%f", win, window->pos_x,
|
|
window->pos_y);
|
|
if ([self windowSurfaceHasRole:window] == NO)
|
|
{
|
|
[self createSurfaceShell:window];
|
|
}
|
|
NSRect rect = NSMakeRect(window->pos_x, window->pos_y, window->width,
|
|
window->height);
|
|
[window->instance flushwindowrect:rect:window->window_id];
|
|
}
|
|
wl_display_dispatch_pending(window->wlconfig->display);
|
|
wl_display_flush(window->wlconfig->display);
|
|
}
|
|
|
|
- (void)movewindow:(NSPoint)loc:(int)win
|
|
{
|
|
NSDebugLog(@"movewindow");
|
|
}
|
|
|
|
- (NSRect) _OSFrameToWFrame: (NSRect)o for: (void*)win
|
|
{
|
|
struct window *window = (struct window *) win;
|
|
NSRect x;
|
|
|
|
x.size.width = o.size.width;
|
|
x.size.height = o.size.height;
|
|
x.origin.x = o.origin.x;
|
|
x.origin.y = o.origin.y + o.size.height;
|
|
x.origin.y = window->output->height - x.origin.y;
|
|
return x;
|
|
}
|
|
|
|
- (void)placewindow:(NSRect)rect:(int)win
|
|
{
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
|
|
NSDebugLog(@"[%d] placewindow: %@", win, NSStringFromRect(rect));
|
|
WaylandConfig *config = window->wlconfig;
|
|
|
|
NSRect frame;
|
|
NSRect wframe;
|
|
BOOL resize = NO;
|
|
BOOL move = NO;
|
|
|
|
frame = [(GSWindowWithNumber(window->window_id)) frame];
|
|
if (NSEqualRects(rect, frame) == YES)
|
|
return;
|
|
if (NSEqualSizes(rect.size, frame.size) == NO)
|
|
{
|
|
resize = YES;
|
|
move = YES;
|
|
}
|
|
if (NSEqualPoints(rect.origin, frame.origin) == NO)
|
|
{
|
|
move = YES;
|
|
}
|
|
|
|
wframe = [self _OSFrameToWFrame: rect for: window];
|
|
|
|
if (config->pointer.focus
|
|
&& config->pointer.focus->window_id == window->window_id)
|
|
{
|
|
config->pointer.y -= (wframe.origin.y - window->pos_y);
|
|
config->pointer.x -= (wframe.origin.x - window->pos_x);
|
|
}
|
|
|
|
NSDebugLog(@"[%d] placewindow: oldpos=%fx%f", win, window->pos_x,
|
|
window->pos_y);
|
|
window->width = rect.size.width;
|
|
window->height = rect.size.height;
|
|
window->pos_x = rect.origin.x;
|
|
window->pos_y = NSToWayland(window, rect.origin.y);
|
|
|
|
[window->instance flushwindowrect:rect:window->window_id];
|
|
if (window->xdg_surface)
|
|
{
|
|
xdg_surface_set_window_geometry(window->xdg_surface, 0, 0,
|
|
window->width, window->height);
|
|
wl_surface_commit(window->surface);
|
|
}
|
|
|
|
if (resize == YES)
|
|
{
|
|
NSDebugLog(@"[%d] placewindow: newsize=%fx%f", win, window->width,
|
|
window->height);
|
|
NSEvent *ev = [NSEvent otherEventWithType:NSAppKitDefined
|
|
location:rect.origin
|
|
modifierFlags:0
|
|
timestamp:0
|
|
windowNumber:win
|
|
context:GSCurrentContext()
|
|
subtype:GSAppKitWindowResized
|
|
data1:rect.size.width
|
|
data2:rect.size.height];
|
|
NSDebugLog(@"notify resize=%fx%f", rect.size.width,
|
|
rect.size.height);
|
|
[(GSWindowWithNumber(window->window_id)) sendEvent:ev];
|
|
NSDebugLog(@"notified resize=%fx%f", rect.size.width,
|
|
rect.size.height);
|
|
// we have a new buffer
|
|
}
|
|
else if (move == YES)
|
|
{
|
|
NSDebugLog(@"[%d] placewindow: newpos=%fx%f", win, window->pos_x,
|
|
window->pos_y);
|
|
}
|
|
wl_display_dispatch_pending(window->wlconfig->display);
|
|
wl_display_flush(window->wlconfig->display);
|
|
}
|
|
|
|
- (NSRect)windowbounds:(int)win
|
|
{
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
NSDebugLog(@"windowbounds: win=%d, pos=%dx%d size=%dx%d", window->window_id,
|
|
window->pos_x, window->pos_y, window->width, window->height);
|
|
|
|
return NSMakeRect(window->pos_x, window->output->height - window->pos_y,
|
|
window->width, window->height);
|
|
}
|
|
|
|
- (void)setParentWindow:(int)parentWin forChildWindow:(int)childWin
|
|
{
|
|
if (parentWin == 0)
|
|
{
|
|
return;
|
|
}
|
|
NSDebugLog(@"setParentWindow: parent=%d child=%d", parentWin, childWin);
|
|
struct window *parent = get_window_with_id(wlconfig, parentWin);
|
|
struct window *child = get_window_with_id(wlconfig, childWin);
|
|
|
|
[self createPopupShell:child withParentShell:parent];
|
|
}
|
|
|
|
- (void)setwindowlevel:(int)level:(int)win
|
|
{
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
window->level = level;
|
|
|
|
NSDebugLog(@"setwindowlevel: level=%d win=%d", level, win);
|
|
}
|
|
|
|
- (int)windowlevel:(int)win
|
|
{
|
|
NSDebugLog(@"windowlevel: %d", win);
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
return window->level;
|
|
}
|
|
|
|
- (int)windowdepth:(int)win
|
|
{
|
|
NSDebugLog(@"windowdepth");
|
|
return 0;
|
|
}
|
|
|
|
- (void)setmaxsize:(NSSize)size:(int)win
|
|
{
|
|
// NSDebugLog(@"setmaxsize");
|
|
}
|
|
|
|
- (void)setminsize:(NSSize)size:(int)win
|
|
{
|
|
// NSDebugLog(@"setminsize");
|
|
}
|
|
|
|
- (void)setresizeincrements:(NSSize)size:(int)win
|
|
{
|
|
// NSDebugLog(@"setresizeincrements");
|
|
}
|
|
|
|
- (void)flushwindowrect:(NSRect)rect:(int)win
|
|
{
|
|
NSDebugLog(@"[%d] flushwindowrect: %f,%f %fx%f", win, NSMinX(rect),
|
|
NSMinY(rect), NSWidth(rect), NSHeight(rect));
|
|
struct window *window = get_window_with_id(wlconfig, win);
|
|
|
|
[[GSCurrentContext() class] handleExposeRect:rect forDriver:window->wcs];
|
|
}
|
|
|
|
- (void)styleoffsets:(float *)
|
|
l:(float *)r
|
|
:(float *)t
|
|
:(float *)b
|
|
:(unsigned int)style
|
|
{
|
|
NSDebugLog(@"styleoffsets");
|
|
/* XXX - Assume we don't decorate */
|
|
*l = *r = *t = *b = 0.0;
|
|
}
|
|
|
|
- (void)docedited:(int)edited:(int)win
|
|
{
|
|
// NSDebugLog(@"docedited");
|
|
}
|
|
|
|
- (void)setinputstate:(int)state:(int)win
|
|
{
|
|
// NSDebugLog(@"setinputstate");
|
|
}
|
|
|
|
- (void)setinputfocus:(int)win
|
|
{
|
|
// NSDebugLog(@"setinputfocus");
|
|
}
|
|
|
|
- (void)setalpha:(float)alpha:(int)win
|
|
{
|
|
// NSDebugLog(@"setalpha");
|
|
}
|
|
|
|
- (void)setShadow:(BOOL)hasShadow:(int)win
|
|
{
|
|
// NSDebugLog(@"setshadow");
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation
|
|
WaylandServer (SurfaceRoles)
|
|
- (void)createSurfaceShell:(struct window *)window
|
|
{
|
|
int win = window->window_id;
|
|
NSDebugLog(@"[%d] createSurfaceShell", win);
|
|
|
|
switch (window->level)
|
|
{
|
|
case NSMainMenuWindowLevel:
|
|
NSDebugLog(@"[%d] NSMainMenuWindowLevel", win);
|
|
[self createLayerShell:window
|
|
withLayerType:ZWLR_LAYER_SHELL_V1_LAYER_TOP
|
|
withNamespace:@"gnustep-mainmenu"];
|
|
break;
|
|
case NSSubmenuWindowLevel:
|
|
NSDebugLog(@"[%d] NSSubmenuWindowLevel", win);
|
|
[self createSubMenuShell:window];
|
|
break;
|
|
case NSDesktopWindowLevel:
|
|
NSDebugLog(@"[%d] NSDesktopWindowLevel", win);
|
|
[self createLayerShell:window
|
|
withLayerType:ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND
|
|
withNamespace:@"gnustep-desktop"];
|
|
break;
|
|
case NSPopUpMenuWindowLevel:
|
|
NSDebugLog(@"[%d] NSPopUpMenuWindowLevel", win);
|
|
[self createPopup:win];
|
|
break;
|
|
case NSScreenSaverWindowLevel:
|
|
NSDebugLog(@"[%d] NSScreenSaverWindowLevel", win);
|
|
[self createLayerShell:window
|
|
withLayerType:ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY
|
|
withNamespace:@"gnustep-screensaver"];
|
|
break;
|
|
case NSStatusWindowLevel:
|
|
NSDebugLog(@"[%d] NSStatusWindowLevel", win);
|
|
case NSFloatingWindowLevel:
|
|
NSDebugLog(@"[%d] NSFloatingWindowLevel", win);
|
|
case NSModalPanelWindowLevel:
|
|
NSDebugLog(@"[%d] NSModalPanelWindowLevel", win);
|
|
case NSNormalWindowLevel:
|
|
NSDebugLog(@"[%d] NSNormalWindowLevel", win);
|
|
[self createTopLevel:window];
|
|
|
|
break;
|
|
}
|
|
window->is_out = 0;
|
|
}
|
|
|
|
- (BOOL)windowSurfaceHasRole:(struct window *)window
|
|
{
|
|
return window->toplevel != NULL || window->layer_surface != NULL
|
|
|| window->popup != NULL;
|
|
}
|
|
|
|
- (void)createTopLevel:(struct window *)window
|
|
{
|
|
int win = window->window_id;
|
|
NSDebugLog(@"[%d] createTopLevel", win);
|
|
|
|
if ([self windowSurfaceHasRole:window])
|
|
{
|
|
// if the role is already assigned skip
|
|
return;
|
|
}
|
|
|
|
if (window->surface)
|
|
{
|
|
wl_surface_destroy(window->surface);
|
|
}
|
|
window->surface = wl_compositor_create_surface(wlconfig->compositor);
|
|
|
|
if (!window->surface)
|
|
{
|
|
NSDebugLog(@"can't create wayland surface");
|
|
free(window);
|
|
return;
|
|
}
|
|
|
|
wl_surface_set_user_data(window->surface, window);
|
|
if (window->xdg_surface == NULL)
|
|
{
|
|
window->xdg_surface
|
|
= xdg_wm_base_get_xdg_surface(wlconfig->wm_base, window->surface);
|
|
window->toplevel = xdg_surface_get_toplevel(window->xdg_surface);
|
|
|
|
xdg_toplevel_add_listener(window->toplevel, &xdg_toplevel_listener,
|
|
window);
|
|
xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener,
|
|
window);
|
|
|
|
xdg_surface_set_window_geometry(window->xdg_surface, 0, 0, window->width,
|
|
window->height);
|
|
}
|
|
|
|
wl_surface_commit(window->surface);
|
|
wl_display_dispatch_pending(window->wlconfig->display);
|
|
wl_display_flush(window->wlconfig->display);
|
|
}
|
|
|
|
- (void)createLayerShell:(struct window *)window
|
|
withLayerType:(enum zwlr_layer_shell_v1_layer)layerType
|
|
withNamespace:(NSString *)namespace
|
|
{
|
|
int win = window->window_id;
|
|
NSDebugLog(@"[%d] createLayerShell: %@", win, namespace);
|
|
|
|
if ([self windowSurfaceHasRole:window])
|
|
{
|
|
// if the role is already assigned skip
|
|
return;
|
|
}
|
|
|
|
if (!wlconfig->layer_shell)
|
|
{
|
|
NSDebugLog(@"layer shell not supported, fallback to xdg toplevel");
|
|
[self createTopLevel:window];
|
|
return;
|
|
}
|
|
|
|
if (window->surface)
|
|
{
|
|
wl_surface_destroy(window->surface);
|
|
}
|
|
|
|
window->surface = wl_compositor_create_surface(wlconfig->compositor);
|
|
wl_surface_set_user_data(window->surface, window);
|
|
|
|
const char *cString = [namespace UTF8String];
|
|
window->layer_surface
|
|
= zwlr_layer_shell_v1_get_layer_surface(wlconfig->layer_shell,
|
|
window->surface,
|
|
window->output->output, layerType,
|
|
cString);
|
|
assert(window->layer_surface);
|
|
zwlr_layer_surface_v1_set_size(window->layer_surface, window->width,
|
|
window->height);
|
|
|
|
zwlr_layer_surface_v1_set_anchor(window->layer_surface,
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
|
|
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
|
|
|
|
zwlr_layer_surface_v1_set_exclusive_zone(window->layer_surface, 1);
|
|
|
|
zwlr_layer_surface_v1_set_margin(window->layer_surface, window->pos_y, 0, 0,
|
|
window->pos_x);
|
|
|
|
NSDebugLog(@"layer margins: %f,%f,%f,%f", window->pos_y, 0.0f, 0.0f,
|
|
window->pos_x);
|
|
zwlr_layer_surface_v1_add_listener(window->layer_surface,
|
|
&layer_surface_listener, window);
|
|
|
|
wl_surface_commit(window->surface);
|
|
wl_display_dispatch_pending(window->wlconfig->display);
|
|
wl_display_flush(window->wlconfig->display);
|
|
}
|
|
- (struct window *)getSuperMenuWindow:(struct window *)window
|
|
{
|
|
NSMenuPanel *nswin = (GSWindowWithNumber(window->window_id));
|
|
if (!nswin)
|
|
{
|
|
NSDebugLog(@"makeSubmenu can't find nswin");
|
|
return NULL;
|
|
}
|
|
NSMenu *menu = [nswin menu];
|
|
if (!menu)
|
|
{
|
|
NSDebugLog(@"makeSubmenu can't find menu");
|
|
return NULL;
|
|
}
|
|
NSMenu *supermenu = [menu supermenu];
|
|
if (!supermenu)
|
|
{
|
|
NSDebugLog(@"makeSubmenu can't find supermenu");
|
|
return NULL;
|
|
}
|
|
NSWindow *parent = [supermenu window];
|
|
if (!parent)
|
|
{
|
|
NSDebugLog(@"makeSubmenu can't find the parent");
|
|
return NULL;
|
|
}
|
|
|
|
struct window *parentwindow
|
|
= get_window_with_id(wlconfig, [parent windowNumber]);
|
|
|
|
return parentwindow;
|
|
}
|
|
|
|
- (void)createSubMenuShell:(struct window *)window
|
|
{
|
|
int win = window->window_id;
|
|
|
|
if ([self windowSurfaceHasRole:window])
|
|
{
|
|
// if the role is already assigned skip
|
|
return;
|
|
}
|
|
|
|
if (wlconfig->layer_shell)
|
|
{
|
|
// the preferred way to create a submenu is to use an overlay
|
|
[self createLayerShell:window
|
|
withLayerType:ZWLR_LAYER_SHELL_V1_LAYER_TOP
|
|
withNamespace:@"gnustep-submenu"];
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
NSDebugLog(@"layer shell not supported, fallback to xdg popup");
|
|
}
|
|
// if the layer shell is not available then we use the xdg popup
|
|
// for that we need a parent toplevel window
|
|
|
|
struct window *rootwindow = window; //[self getSuperMenuWindow: window];
|
|
struct window *parentwindow = rootwindow;
|
|
while (rootwindow = [self getSuperMenuWindow:parentwindow])
|
|
{
|
|
parentwindow = rootwindow;
|
|
}
|
|
if (!parentwindow)
|
|
{
|
|
//[self createTopLevel: window];
|
|
return;
|
|
}
|
|
NSDebugLog(@"new popup: %d parent id: %d", win, parentwindow->window_id);
|
|
NSDebugLog(@"parent: %d [%f,%f %fx%f]", parentwindow->window_id,
|
|
parentwindow->pos_x, parentwindow->pos_y, parentwindow->width,
|
|
parentwindow->height);
|
|
|
|
[self createPopupShell:window withParentShell:parentwindow];
|
|
}
|
|
|
|
- (void)createPopup:(struct window *)window
|
|
{
|
|
NSDebugLog(@"createPopup noop");
|
|
}
|
|
|
|
- (void)createPopupShell:(struct window *)child
|
|
withParentShell:(struct window *)parent
|
|
{
|
|
NSDebugLog(@"createPopupShell");
|
|
|
|
if (parent->xdg_surface == NULL || parent->toplevel == NULL)
|
|
{
|
|
NSDebugLog(@"parent surface %d is not toplevel", parent->window_id);
|
|
return;
|
|
}
|
|
if ([self windowSurfaceHasRole:child])
|
|
{
|
|
[self destroySurfaceRole:child];
|
|
}
|
|
|
|
child->surface = wl_compositor_create_surface(wlconfig->compositor);
|
|
wl_surface_set_user_data(child->surface, child);
|
|
|
|
NSWindow *nswin = (GSWindowWithNumber(child->window_id));
|
|
CGFloat x = nswin.frame.origin.x;
|
|
CGFloat y = nswin.frame.origin.y;
|
|
CGFloat width = nswin.frame.size.width;
|
|
CGFloat height = nswin.frame.size.height;
|
|
|
|
child->xdg_surface
|
|
= xdg_wm_base_get_xdg_surface(wlconfig->wm_base, child->surface);
|
|
struct xdg_positioner *positioner
|
|
= xdg_wm_base_create_positioner(wlconfig->wm_base);
|
|
|
|
xdg_positioner_set_size(positioner, child->width, child->height);
|
|
|
|
xdg_positioner_set_anchor_rect(positioner, 0.0, 0.0, parent->width,
|
|
parent->height);
|
|
xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
|
|
xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
|
|
|
|
xdg_positioner_set_offset(positioner, (child->pos_x - parent->pos_x),
|
|
(child->pos_y - parent->pos_y));
|
|
|
|
// NSDebugLog(@"xdg_positioner offset: %f,%f",
|
|
// (child->pos_x - parent->pos_x), (child->pos_y - parent->pos_y));
|
|
|
|
child->popup = xdg_surface_get_popup(child->xdg_surface, parent->xdg_surface,
|
|
positioner);
|
|
|
|
xdg_popup_add_listener(child->popup, &xdg_popup_listener, child);
|
|
xdg_surface_add_listener(child->xdg_surface, &xdg_surface_listener, child);
|
|
|
|
xdg_surface_set_window_geometry(child->xdg_surface, 0, 0, child->width,
|
|
child->height);
|
|
|
|
NSDebugLog(@"child_geometry : %f,%f %fx%f", child->pos_x - parent->pos_x,
|
|
child->pos_y - parent->pos_y, child->width, child->height);
|
|
wl_surface_commit(child->surface);
|
|
wl_display_dispatch_pending(wlconfig->display);
|
|
wl_display_flush(wlconfig->display);
|
|
}
|
|
|
|
- (void)destroySurfaceRole:(struct window *)window
|
|
{
|
|
NSDebugLog(@"[%d] destroySurfaceRole", window->window_id);
|
|
|
|
if (window == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (window->layer_surface)
|
|
{
|
|
zwlr_layer_surface_v1_destroy(window->layer_surface);
|
|
window->layer_surface = NULL;
|
|
}
|
|
if (window->xdg_surface)
|
|
{
|
|
if (window->toplevel)
|
|
{
|
|
xdg_toplevel_destroy(window->toplevel);
|
|
window->toplevel = NULL;
|
|
}
|
|
if (window->popup)
|
|
{
|
|
xdg_popup_destroy(window->popup);
|
|
window->popup = NULL;
|
|
}
|
|
xdg_surface_destroy(window->xdg_surface);
|
|
window->xdg_surface = NULL;
|
|
}
|
|
window->configured = NO;
|
|
}
|
|
|
|
- (void)destroyWindowShell:(struct window *)window
|
|
{
|
|
NSDebugLog(@"[%d] destroyWindowShell", window->window_id);
|
|
|
|
[self destroySurfaceRole:window];
|
|
|
|
window->is_out = 1;
|
|
|
|
wl_display_dispatch_pending(window->wlconfig->display);
|
|
wl_display_flush(window->wlconfig->display);
|
|
}
|
|
|
|
@end
|