summaryrefslogtreecommitdiff
path: root/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx')
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx1428
1 files changed, 1428 insertions, 0 deletions
diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
new file mode 100644
index 000000000..71a14ca22
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
@@ -0,0 +1,1428 @@
+//
+// Implementation of Wayland Screen interface
+//
+// Copyright 1998-2022 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#include "Fl_Wayland_Screen_Driver.H"
+#include "Fl_Wayland_Window_Driver.H"
+#include "Fl_Wayland_System_Driver.H"
+#include "Fl_Wayland_Graphics_Driver.H"
+#include <wayland-cursor.h>
+#include "../../../libdecor/src/libdecor.h"
+#include "xdg-shell-client-protocol.h"
+#include "../Posix/Fl_Posix_System_Driver.H"
+#include <FL/Fl.H>
+#include <FL/platform.H>
+#include <FL/fl_ask.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Tooltip.H>
+#include <FL/filename.H>
+#include "../../print_button.h"
+#include <dlfcn.h>
+#include <sys/time.h>
+#include <linux/input.h>
+#include <stdlib.h>
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-compose.h>
+#include "text-input-client-protocol.h"
+#include <assert.h>
+#include <sys/mman.h>
+extern "C" {
+ bool libdecor_get_cursor_settings(char **theme, int *size);
+}
+
+
+#define fl_max(a,b) ((a) > (b) ? (a) : (b))
+
+struct pointer_output {
+ Fl_Wayland_Screen_Driver::output* output;
+ struct wl_list link;
+};
+
+/* Implementation note about support of 3 Wayland compositors: Mutter, Weston, KDE.
+
+- About CSD and SSD :
+ * Mutter and Weston use CSD (client-side decoration) which means that libdecor.so draws all window
+ titlebars and responds to resize, minimization and maximization events.
+ * KDE uses SSD (server-side decoration) which means the OS draws titlebars according to its own rules
+ and triggers resize, minimization and maximization events.
+
+- Function registry_handle_global() runs within fl_open_display() and sets public static variable
+ Fl_Wayland_Screen_Driver::compositor to either Fl_Wayland_Screen_Driver::MUTTER, ::WESTON, or ::KDE.
+
+- Specific operations for WESTON:
+ * When a libdecor-framed window is minimized under Weston, the frame remains on display. To avoid
+ that, function libdecor_frame_set_minimized() is modified so it turns off the frame's visibility, with
+ function libdecor_frame_set_visibility(), when the window is minimized. That's implemented in file
+ libdecor/build/fl_libdecor.c. The modified libdecor_frame_set_minimized() function, part of libdecor.so,
+ needs access to variable Fl_Wayland_Screen_Driver::compositor, part of libfltk.a. This is achieved
+ calling FLTK function fl_libdecor_using_weston() which returns whether the running compositor
+ is Weston. This Weston bug has been corrected in Weston version 10. Thus, this special processing
+ is not performed when Weston version is ≥ 10.
+
+- Synchronization between drawing to buffer and committing buffer to screen.
+ Before committing a new graphics scene for display, Wayland requires to make sure the compositor is
+ ready for commit. FLTK uses frame callbacks for that.
+ A frame callback is created when an app calls Fl_Wayland_Window_Driver::make_current()
+ directly. This directs a callback listener function, called surface_frame_done, to be called by the
+ compositor when it's ready to commit a new graphics scene. This function schedules a new frame callback
+ and commits the buffer to the display.
+ A frame callback is also created by Fl_Wayland_Window_Driver::flush() when a window redraw operation
+ is needed. FLTK processes wayland events until the compositor is ready for commit and then commits
+ the new window content.
+
+ - Support of Fl_Window::border(int) :
+ FLTK uses libdecor_frame_set_visibility() to show or hide a toplevel window's frame. This doesn't work
+ with KDE which uses Server-Side Decoration. In that case, FLTK hides and re-shows the window to toggle
+ between presence and absence of a window's frame.
+*/
+
+
+/* Implementation note about screen-related information
+
+ struct wl_output : Wayland-defined, contains info about a screen, one such record for each screen
+
+ struct Fl_Wayland_Screen_Driver::output { // FLTK defined
+ uint32_t id; // screen identification
+ short x_org;
+ short y_org;
+ short width; // screen width in pixels
+ short height; // screen height in pixels
+ float dpi;
+ struct wl_output *wl_output;
+ int wld_scale; // Wayland scale
+ float gui_scale; // user-set scale
+ struct wl_list link;
+ };
+
+ struct Fl_Wayland_Window_Driver::window_output { // FLTK defined
+ Fl_Wayland_Screen_Driver::output* output;
+ struct wl_list link;
+ }
+
+ The unique Fl_Wayland_Screen_Driver object contains a member
+ "outputs" of type struct wl_list = list of Fl_Wayland_Screen_Driver::output records
+ - this list is initialised by open-display
+ - registry_handle_global() feeds the list with 1 record for each screen
+ - registry_handle_global_remove() runs when a screen is removed. It removes
+ the output record that corresponds to that screen from the unique list of screens
+ (outputs member of the Fl_Wayland_Screen_Driver) and the list of struct output objects attached
+ to each window.
+
+ Each Fl_Wayland_Window_Driver object contains a member
+ "outputs" of type struct wl_list = list of Fl_Wayland_Window_Driver::window_output records
+ - this list is fed by surface_enter() (when a surface is mapped)
+ - these records contain:
+ window_output->output = (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output);
+ where wl_output is received from OS by surface_enter()
+ - surface_leave() removes the adequate record from the list
+ - hide() empties the list
+ - Fl_Wayland_Window_Driver::update_scale() sets the scale info of the records for a given window
+ */
+
+Fl_Wayland_Screen_Driver::compositor_name Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::unspecified;
+
+extern "C" {
+ FL_EXPORT bool fl_libdecor_using_weston(void) {
+ return Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::WESTON;
+ };
+}
+
+static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
+{
+ xdg_wm_base_pong(xdg_wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = xdg_wm_base_ping,
+};
+
+
+// these are set by Fl::args() and override any system colors: from Fl_get_system_colors.cxx
+extern const char *fl_fg;
+extern const char *fl_bg;
+extern const char *fl_bg2;
+// end of extern additions workaround
+
+
+/**
+ Creates a driver that manages all screen and display related calls.
+
+ This function must be implemented once for every platform.
+ */
+Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver()
+{
+ return new Fl_Wayland_Screen_Driver();
+}
+
+FL_EXPORT struct wl_display *fl_display = NULL;
+
+static bool has_xrgb = false;
+
+
+static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ if (format == Fl_Wayland_Graphics_Driver::wld_format)
+ has_xrgb = true;
+}
+
+static struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void do_set_cursor(struct seat *seat, struct wl_cursor *wl_cursor = NULL)
+{
+ struct wl_cursor_image *image;
+ struct wl_buffer *buffer;
+ const int scale = seat->pointer_scale;
+
+ if (!seat->cursor_theme)
+ return;
+
+ if (!wl_cursor) wl_cursor = seat->default_cursor;
+ image = wl_cursor->images[0];
+ buffer = wl_cursor_image_get_buffer(image);
+ wl_pointer_set_cursor(seat->wl_pointer, seat->serial,
+ seat->cursor_surface,
+ image->hotspot_x / scale,
+ image->hotspot_y / scale);
+ wl_surface_attach(seat->cursor_surface, buffer, 0, 0);
+ wl_surface_set_buffer_scale(seat->cursor_surface, scale);
+ wl_surface_damage_buffer(seat->cursor_surface, 0, 0,
+ image->width, image->height);
+ wl_surface_commit(seat->cursor_surface);
+}
+
+static uint32_t ptime;
+FL_EXPORT uint32_t fl_event_time;
+static int px, py;
+
+
+static void set_event_xy(Fl_Window *win) {
+ // turn off is_click if enough time or mouse movement has passed:
+ if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 ||
+ fl_event_time >= ptime+1000) {
+ Fl::e_is_click = 0;
+//fprintf(stderr, "Fl::e_is_click = 0\n");
+ }
+}
+
+// if this is same event as last && is_click, increment click count:
+static inline void checkdouble() {
+ if (Fl::e_is_click == Fl::e_keysym) {
+ Fl::e_clicks++;
+//fprintf(stderr, "Fl::e_clicks = %d\n", Fl::e_clicks);
+ } else {
+ Fl::e_clicks = 0;
+ Fl::e_is_click = Fl::e_keysym;
+//fprintf(stderr, "Fl::e_is_click = %d\n", Fl::e_is_click);
+ }
+ px = Fl::e_x_root;
+ py = Fl::e_y_root;
+ ptime = fl_event_time;
+}
+
+
+Fl_Window *Fl_Wayland_Screen_Driver::surface_to_window(struct wl_surface *surface) {
+ Fl_X *xp = Fl_X::first;
+ while (xp) {
+ if (xp->xid->wl_surface == surface) return xp->w;
+ xp = xp->next;
+ }
+ return NULL;
+}
+
+
+static void pointer_enter(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t serial,
+ struct wl_surface *surface,
+ wl_fixed_t surface_x,
+ wl_fixed_t surface_y)
+{
+ struct seat *seat = (struct seat*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ struct wl_cursor *cursor = NULL;
+ if (win) { // use custom cursor if present
+ Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win);
+ cursor = driver->cursor();
+ if (win->parent() && !cursor) {
+ driver = Fl_Wayland_Window_Driver::driver(win->top_window());
+ cursor = driver->cursor();
+ }
+ }
+ do_set_cursor(seat, cursor);
+ seat->serial = serial;
+ if (win) {
+ float f = Fl::screen_scale(win->screen_num());
+ Fl::e_x = wl_fixed_to_int(surface_x) / f;
+ Fl::e_x_root = Fl::e_x + win->x();
+ Fl::e_y = wl_fixed_to_int(surface_y) / f;
+ Fl::e_y_root = Fl::e_y + win->y();
+ set_event_xy(win);
+ Fl::handle(FL_ENTER, win);
+//fprintf(stderr, "pointer_enter window=%p\n", win);
+ }
+ seat->pointer_focus = surface;
+}
+
+
+static void pointer_leave(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t serial,
+ struct wl_surface *surface)
+{
+ struct seat *seat = (struct seat*)data;
+ if (seat->pointer_focus == surface) seat->pointer_focus = NULL;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ if (win) {
+ Fl::belowmouse(0);
+ set_event_xy(win);
+ }
+//fprintf(stderr, "pointer_leave surface=%p window=%p\n", surface, win);
+}
+
+
+static void pointer_motion(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t time,
+ wl_fixed_t surface_x,
+ wl_fixed_t surface_y)
+{
+ struct seat *seat = (struct seat*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(seat->pointer_focus);
+ if (!win) return;
+ float f = Fl::screen_scale(win->screen_num());
+ Fl::e_x = wl_fixed_to_int(surface_x) / f;
+ Fl::e_x_root = Fl::e_x + win->x();
+ // If there's an active grab() and the pointer is in a window other than the grab(),
+ // make e_x_root too large to be in any window
+ if (Fl::grab() && !Fl::grab()->menu_window() && Fl::grab() != win) {
+ Fl::e_x_root = 1000000;
+ }
+ Fl::e_y = wl_fixed_to_int(surface_y) / f;
+ Fl::e_y_root = Fl::e_y + win->y();
+//fprintf(stderr, "FL_MOVE on win=%p to x:%dx%d root:%dx%d\n", win, Fl::e_x, Fl::e_y, Fl::e_x_root, Fl::e_y_root);
+ fl_event_time = time;
+ set_event_xy(win);
+ Fl::handle(FL_MOVE, win);
+}
+
+
+//#include <FL/names.h>
+static void pointer_button(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t button,
+ uint32_t state)
+{
+ struct seat *seat = (struct seat*)data;
+ seat->serial = serial;
+ int event = 0;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(seat->pointer_focus);
+ if (!win) return;
+ fl_event_time = time;
+ if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED && seat->pointer_focus == NULL &&
+ fl_xid(win)->kind == Fl_Wayland_Window_Driver::DECORATED) {
+ // click on titlebar
+ libdecor_frame_move(fl_xid(win)->frame, seat->wl_seat, serial);
+ return;
+ }
+ int b = 0;
+ Fl::e_state = 0;
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ if (button == BTN_LEFT) {Fl::e_state = FL_BUTTON1; b = 1;}
+ else if (button == BTN_RIGHT) {Fl::e_state = FL_BUTTON3; b = 3;}
+ else if (button == BTN_MIDDLE) {Fl::e_state = FL_BUTTON2; b = 2;}
+ Fl::e_keysym = FL_Button + b;
+ }
+ Fl::e_dx = Fl::e_dy = 0;
+
+ set_event_xy(win);
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ event = FL_PUSH;
+ checkdouble();
+ } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) event = FL_RELEASE;
+//fprintf(stderr, "%s %s\n", fl_eventnames[event], win->label() ? win->label():"[]");
+ Fl::handle(event, win);
+}
+
+static void pointer_axis(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t time,
+ uint32_t axis,
+ wl_fixed_t value)
+{
+ struct seat *seat = (struct seat*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(seat->pointer_focus);
+ if (!win) return;
+ fl_event_time = time;
+ int delta = wl_fixed_to_int(value) / 10;
+//fprintf(stderr, "FL_MOUSEWHEEL: %c delta=%d\n", axis==WL_POINTER_AXIS_HORIZONTAL_SCROLL?'H':'V', delta);
+ // allow both horizontal and vertical movements to be processed by the widget
+ if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
+ Fl::e_dx = delta;
+ Fl::e_dy = 0;
+ Fl::handle(FL_MOUSEWHEEL, win->top_window());
+ }
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ Fl::e_dx = 0;
+ Fl::e_dy = delta;
+ Fl::handle(FL_MOUSEWHEEL, win->top_window());
+ }
+}
+
+static struct wl_pointer_listener pointer_listener = {
+ pointer_enter,
+ pointer_leave,
+ pointer_motion,
+ pointer_button,
+ pointer_axis
+};
+
+static const char *proxy_tag = "FLTK for Wayland";
+
+bool Fl_Wayland_Screen_Driver::own_output(struct wl_output *output)
+{
+ return wl_proxy_get_tag((struct wl_proxy *)output) == &proxy_tag;
+}
+
+static void init_cursors(struct seat *seat);
+
+static void try_update_cursor(struct seat *seat)
+{
+ struct pointer_output *pointer_output;
+ int scale = 1;
+
+ wl_list_for_each(pointer_output, &seat->pointer_outputs, link) {
+ scale = fl_max(scale, pointer_output->output->wld_scale);
+ }
+
+ if (scale != seat->pointer_scale) {
+ seat->pointer_scale = scale;
+ init_cursors(seat);
+ do_set_cursor(seat);
+ }
+}
+
+
+static void cursor_surface_enter(void *data,
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
+{
+ struct seat *seat = (struct seat*)data;
+ struct pointer_output *pointer_output;
+
+ if (!Fl_Wayland_Screen_Driver::own_output(wl_output))
+ return;
+
+ pointer_output = (struct pointer_output *)calloc(1, sizeof(struct pointer_output));
+ pointer_output->output = (Fl_Wayland_Screen_Driver::output *)wl_output_get_user_data(wl_output);
+//fprintf(stderr, "cursor_surface_enter: wl_output_get_user_data(%p)=%p\n", wl_output, pointer_output->output);
+ wl_list_insert(&seat->pointer_outputs, &pointer_output->link);
+ try_update_cursor(seat);
+ // maintain custom window cursor
+ Fl_Window *win = Fl::first_window();
+ if (win) {
+ Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win);
+ struct wl_cursor *cursor = driver->cursor();
+ if (cursor) do_set_cursor(seat, cursor);
+ }
+}
+
+static void cursor_surface_leave(void *data,
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
+{
+ struct seat *seat = (struct seat*)data;
+ struct pointer_output *pointer_output, *tmp;
+
+ wl_list_for_each_safe(pointer_output, tmp, &seat->pointer_outputs, link) {
+ if (pointer_output->output->wl_output == wl_output) {
+ wl_list_remove(&pointer_output->link);
+ free(pointer_output);
+ }
+ }
+}
+
+static struct wl_surface_listener cursor_surface_listener = {
+ cursor_surface_enter,
+ cursor_surface_leave,
+};
+
+
+static void init_cursors(struct seat *seat)
+{
+ char *name;
+ int size;
+ struct wl_cursor_theme *theme;
+
+ if (!libdecor_get_cursor_settings(&name, &size)) {
+ name = NULL;
+ size = 24;
+ }
+ size *= seat->pointer_scale;
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ theme = wl_cursor_theme_load(name, size, scr_driver->wl_shm);
+ free(name);
+ //struct wl_cursor_theme *old_theme = seat->cursor_theme;
+ if (theme != NULL) {
+ if (seat->cursor_theme) {
+ // caution to destroy theme because Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor) caches used cursors
+ scr_driver->reset_cursor();
+ wl_cursor_theme_destroy(seat->cursor_theme);
+ }
+ seat->cursor_theme = theme;
+ }
+ if (seat->cursor_theme)
+ seat->default_cursor = scr_driver->xc_arrow = wl_cursor_theme_get_cursor(seat->cursor_theme, "left_ptr");
+ if (!seat->cursor_surface) {
+ seat->cursor_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
+ wl_surface_add_listener(seat->cursor_surface, &cursor_surface_listener, seat);
+ }
+}
+
+
+static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t format, int32_t fd, uint32_t size)
+{
+ struct seat *seat = (struct seat*)data;
+ assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
+
+ char *map_shm = (char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ assert(map_shm != MAP_FAILED);
+
+ struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(seat->xkb_context, map_shm,
+ XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ munmap(map_shm, size);
+ close(fd);
+
+ struct xkb_state *xkb_state = xkb_state_new(xkb_keymap);
+ xkb_keymap_unref(seat->xkb_keymap);
+ xkb_state_unref(seat->xkb_state);
+ seat->xkb_keymap = xkb_keymap;
+ seat->xkb_state = xkb_state;
+}
+
+static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ struct seat *seat = (struct seat*)data;
+//fprintf(stderr, "keyboard enter fl_win=%p; keys pressed are:\n", Fl_Wayland_Screen_Driver::surface_to_window(surface));
+ seat->keyboard_surface = surface;
+ seat->keyboard_enter_serial = serial;
+}
+
+struct key_repeat_data_t {
+ uint32_t time;
+ Fl_Window *window;
+};
+
+#define KEY_REPEAT_DELAY 0.5 // sec
+#define KEY_REPEAT_INTERVAL 0.05 // sec
+
+static void key_repeat_timer_cb(key_repeat_data_t *key_repeat_data) {
+ if ((Fl::event() == FL_KEYDOWN || (Fl_Window_Driver::menu_parent() && Fl::event() == FL_ENTER)) && fl_event_time == key_repeat_data->time) {
+ Fl::handle(FL_KEYDOWN, key_repeat_data->window);
+ Fl::add_timeout(KEY_REPEAT_INTERVAL, (Fl_Timeout_Handler)key_repeat_timer_cb, key_repeat_data);
+ }
+ else delete key_repeat_data;
+}
+
+int Fl_Wayland_Screen_Driver::next_marked_length = 0;
+
+int Fl_Wayland_Screen_Driver::has_marked_text() const {
+ return 1;
+}
+
+int Fl_Wayland_Screen_Driver::insertion_point_x = 0;
+int Fl_Wayland_Screen_Driver::insertion_point_y = 0;
+int Fl_Wayland_Screen_Driver::insertion_point_width = 0;
+int Fl_Wayland_Screen_Driver::insertion_point_height = 0;
+bool Fl_Wayland_Screen_Driver::insertion_point_location_is_valid = false;
+
+
+// inform TIM about location of the insertion point, and memorize this info.
+void Fl_Wayland_Screen_Driver::insertion_point_location(int x, int y, int height) {
+//printf("insertion_point_location %dx%d\n",x,y);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (scr_driver->seat->text_input) {
+ if (Fl::focus()) {
+ Fl_Widget *focuswin = Fl::focus()->window();
+ while (focuswin && focuswin->parent()) {
+ x += focuswin->x(); y += focuswin->y();
+ focuswin = focuswin->window();
+ }
+ }
+ float s = fl_graphics_driver->scale();
+ insertion_point_location_is_valid = true;
+ insertion_point_x = s*x;
+ insertion_point_y = s*(y-height);
+ insertion_point_width = s*5;
+ insertion_point_height = s*height;
+ if (zwp_text_input_v3_get_user_data(scr_driver->seat->text_input) ) {
+ zwp_text_input_v3_set_cursor_rectangle(scr_driver->seat->text_input, insertion_point_x,
+ insertion_point_y, insertion_point_width, insertion_point_height);
+ zwp_text_input_v3_commit(scr_driver->seat->text_input);
+ }
+ }
+}
+
+
+// computes window coordinates & size of insertion point
+bool Fl_Wayland_Screen_Driver::insertion_point_location(int *px, int *py, int *pwidth, int *pheight)
+// return true if the current coordinates and size of the insertion point are available
+{
+ if ( ! insertion_point_location_is_valid ) return false;
+ *px = insertion_point_x;
+ *py = insertion_point_y;
+ *pwidth = insertion_point_width;
+ *pheight = insertion_point_height;
+ return true;
+}
+
+int Fl_Wayland_Screen_Driver::compose(int& del) {
+ unsigned char ascii = (unsigned char)Fl::e_text[0];
+ int condition = (Fl::e_state & (FL_ALT | FL_META | FL_CTRL)) && ascii < 128 ; // letter+modifier key
+ condition |= (Fl::e_keysym >= FL_Shift_L && Fl::e_keysym <= FL_Alt_R); // pressing modifier key
+ condition |= (Fl::e_keysym >= FL_Home && Fl::e_keysym <= FL_Help);
+//fprintf(stderr, "compose: condition=%d e_state=%x ascii=%d\n", condition, Fl::e_state, ascii);
+ if (condition) { del = 0; return 0;}
+//fprintf(stderr, "compose: del=%d compose_state=%d next_marked_length=%d \n", del, Fl::compose_state, next_marked_length);
+ del = Fl::compose_state;
+ Fl::compose_state = next_marked_length;
+ // no-underlined-text && (ascii non-printable || ascii == delete)
+ if (ascii && (!Fl::compose_state) && (ascii <= 31 || ascii == 127)) { del = 0; return 0; }
+ return 1;
+}
+
+void Fl_Wayland_Screen_Driver::compose_reset()
+{
+ Fl::compose_state = 0;
+ next_marked_length = 0;
+ xkb_compose_state_reset(seat->xkb_compose_state);
+}
+
+struct dead_key_struct {
+ xkb_keysym_t keysym; // the keysym obtained when hitting a dead key
+ const char *marked_text; // the temporary text to display for that dead key
+};
+
+static dead_key_struct dead_keys[] = {
+ {XKB_KEY_dead_grave, "`"},
+ {XKB_KEY_dead_acute, "´"},
+ {XKB_KEY_dead_circumflex, "^"},
+ {XKB_KEY_dead_tilde, "~"},
+ {XKB_KEY_dead_perispomeni, "~"}, // alias for dead_tilde
+ {XKB_KEY_dead_macron, "¯"},
+ {XKB_KEY_dead_breve, "˘"},
+ {XKB_KEY_dead_abovedot, "˙"},
+ {XKB_KEY_dead_diaeresis, "¨"},
+ {XKB_KEY_dead_abovering, "˚"},
+ {XKB_KEY_dead_doubleacute, "˝"},
+ {XKB_KEY_dead_caron, "ˇ"},
+ {XKB_KEY_dead_cedilla, "¸"},
+ {XKB_KEY_dead_ogonek, "˛"},
+ {XKB_KEY_dead_iota, "ι"},
+ {XKB_KEY_dead_doublegrave, " ̏"},
+};
+
+const int dead_key_count = sizeof(dead_keys)/sizeof(struct dead_key_struct);
+
+static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
+{
+ struct seat *seat = (struct seat*)data;
+ seat->serial = serial;
+ static char buf[128];
+ uint32_t keycode = key + 8;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->xkb_state, keycode);
+/*xkb_keysym_get_name(sym, buf, sizeof(buf));
+const char *action = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release");
+fprintf(stderr, "key %s: sym: %-12s(%d) code:%u fl_win=%p, ", action, buf, sym, keycode, Fl_Wayland_Screen_Driver::surface_to_window(seat->keyboard_surface));*/
+ xkb_state_key_get_utf8(seat->xkb_state, keycode, buf, sizeof(buf));
+//fprintf(stderr, "utf8: '%s' e_length=%d [%d]\n", buf, (int)strlen(buf), *buf);
+ Fl::e_keysym = sym;
+ // special processing for number keys == keycodes 10-19 :
+ if (keycode >= 10 && keycode <= 18) Fl::e_keysym = keycode + 39;
+ else if (keycode == 19) Fl::e_keysym = 48;
+ Fl::e_text = buf;
+ Fl::e_length = strlen(buf);
+ // Process dead keys and compose sequences :
+ enum xkb_compose_status status = XKB_COMPOSE_NOTHING;
+ // This part is useful only if the compositor doesn't support protocol text-input-unstable-v3
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED && !(sym >= FL_Shift_L && sym <= FL_Alt_R) &&
+ sym != XKB_KEY_ISO_Level3_Shift) {
+ xkb_compose_state_feed(seat->xkb_compose_state, sym);
+ status = xkb_compose_state_get_status(seat->xkb_compose_state);
+ if (status == XKB_COMPOSE_COMPOSING) {
+ if (Fl::e_length == 0) { // dead keys produce e_length = 0
+ int i;
+ for (i = 0; i < dead_key_count; i++) {
+ if (dead_keys[i].keysym == sym) break;
+ }
+ if (i < dead_key_count) strcpy(buf, dead_keys[i].marked_text);
+ else buf[0] = 0;
+ Fl::e_length = strlen(buf);
+ Fl::compose_state = 0;
+ }
+ Fl_Wayland_Screen_Driver::next_marked_length = Fl::e_length;
+ } else if (status == XKB_COMPOSE_COMPOSED) {
+ Fl::e_length = xkb_compose_state_get_utf8(seat->xkb_compose_state, buf, sizeof(buf));
+ Fl::compose_state = Fl_Wayland_Screen_Driver::next_marked_length;
+ Fl_Wayland_Screen_Driver::next_marked_length = 0;
+ } else if (status == XKB_COMPOSE_CANCELLED) {
+ Fl::e_length = 0;
+ Fl::compose_state = Fl_Wayland_Screen_Driver::next_marked_length;
+ Fl_Wayland_Screen_Driver::next_marked_length = 0;
+ }
+//fprintf(stderr, "xkb_compose_status=%d ctxt=%p state=%p l=%d[%s]\n", status, seat->xkb_context, seat->xkb_compose_state, Fl::e_length, buf);
+ }
+ // end of part used only without text-input-unstable-v3
+
+ fl_event_time = time;
+ int event = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? FL_KEYDOWN : FL_KEYUP);
+ // Send event to focus-containing top window as defined by FLTK,
+ // otherwise send it to Wayland-defined focus window
+ Fl_Window *win = ( Fl::focus() ? Fl::focus()->top_window() : Fl_Wayland_Screen_Driver::surface_to_window(seat->keyboard_surface) );
+ if (win) {
+ set_event_xy(win);
+ Fl::e_is_click = 0;
+ Fl::handle(event, win);
+ }
+ key_repeat_data_t *key_repeat_data = new key_repeat_data_t;
+ key_repeat_data->time = time;
+ key_repeat_data->window = win;
+ if (event == FL_KEYDOWN && status == XKB_COMPOSE_NOTHING && !(sym >= FL_Shift_L && sym <= FL_Alt_R))
+ Fl::add_timeout(KEY_REPEAT_DELAY, (Fl_Timeout_Handler)key_repeat_timer_cb, key_repeat_data);
+}
+
+static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct seat *seat = (struct seat*)data;
+//fprintf(stderr, "keyboard leave fl_win=%p\n", Fl_Wayland_Screen_Driver::surface_to_window(surface));
+ seat->keyboard_surface = NULL;
+}
+
+static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+ struct seat *seat = (struct seat*)data;
+ xkb_state_update_mask(seat->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+ Fl::e_state = 0;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_DEPRESSED))
+ Fl::e_state |= FL_SHIFT;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_DEPRESSED))
+ Fl::e_state |= FL_CTRL;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_DEPRESSED))
+ Fl::e_state |= FL_ALT;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED))
+ Fl::e_state |= FL_CAPS_LOCK;
+//fprintf(stderr, "mods_depressed=%u Fl::e_state=%X\n", mods_depressed, Fl::e_state);
+}
+
+static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay)
+{
+ // wl_keyboard is version 3 under Debian, but that event isn't sent until version 4
+}
+
+static const struct wl_keyboard_listener wl_keyboard_listener = {
+ .keymap = wl_keyboard_keymap,
+ .enter = wl_keyboard_enter,
+ .leave = wl_keyboard_leave,
+ .key = wl_keyboard_key,
+ .modifiers = wl_keyboard_modifiers,
+ .repeat_info = wl_keyboard_repeat_info,
+};
+
+
+void text_input_enter(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ struct wl_surface *surface) {
+//puts("text_input_enter");
+ zwp_text_input_v3_set_user_data(zwp_text_input_v3, surface);
+ zwp_text_input_v3_enable(zwp_text_input_v3);
+ int x, y, width, height;
+ if (Fl_Wayland_Screen_Driver::insertion_point_location(&x, &y, &width, &height)) {
+ zwp_text_input_v3_set_cursor_rectangle(zwp_text_input_v3, x, y, width, height);
+ }
+ zwp_text_input_v3_commit(zwp_text_input_v3);
+ wl_display_roundtrip(fl_display);
+}
+
+void text_input_leave(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ struct wl_surface *surface) {
+//puts("text_input_leave");
+ zwp_text_input_v3_disable(zwp_text_input_v3);
+ zwp_text_input_v3_set_user_data(zwp_text_input_v3, NULL);
+ zwp_text_input_v3_commit(zwp_text_input_v3);
+}
+
+void text_input_preedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ const char *text, int32_t cursor_begin, int32_t cursor_end) {
+//printf("text_input_preedit_string %s cursor_begin=%d cursor_end=%d\n",text, cursor_begin, cursor_end);
+ // goes to widget as marked text
+ Fl_Wayland_Screen_Driver::next_marked_length = text ? strlen(text) : 0;
+ Fl::e_text = text ? (char*)text : (char*)"";
+ Fl::e_length = text ? strlen(text) : 0;
+ Fl::e_keysym = 'a'; // fake a simple key
+ struct wl_surface *surface = (struct wl_surface*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ set_event_xy(win);
+ Fl::e_is_click = 0;
+ Fl::handle(FL_KEYDOWN, win);
+}
+
+void text_input_commit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ const char *text) {
+//printf("text_input_commit_string %s\n",text);
+ Fl::e_text = (char*)text;
+ Fl::e_length = strlen(text);
+ struct wl_surface *surface = (struct wl_surface*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ set_event_xy(win);
+ Fl::e_is_click = 0;
+ Fl::handle(FL_KEYDOWN, win);
+ zwp_text_input_v3_commit(zwp_text_input_v3);
+ Fl_Wayland_Screen_Driver::next_marked_length = 0;
+ Fl::compose_state = 0;
+}
+
+void text_input_delete_surrounding_text(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ uint32_t before_length, uint32_t after_length) {
+ fprintf(stderr, "delete_surrounding_text before=%d adfter=%d\n",before_length,after_length);
+}
+
+void text_input_done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ uint32_t serial) {
+//puts("text_input_done");
+}
+
+static const struct zwp_text_input_v3_listener text_input_listener = {
+ .enter = text_input_enter,
+ .leave = text_input_leave,
+ .preedit_string = text_input_preedit_string,
+ .commit_string = text_input_commit_string,
+ .delete_surrounding_text = text_input_delete_surrounding_text,
+ .done = text_input_done,
+};
+
+
+static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
+{
+ struct seat *seat = (struct seat*)data;
+ if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) {
+ seat->wl_pointer = wl_seat_get_pointer(wl_seat);
+ wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
+ seat->pointer_scale = 1;
+ init_cursors(seat);
+ } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) {
+ wl_pointer_release(seat->wl_pointer);
+ seat->wl_pointer = NULL;
+ }
+
+ bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;
+ if (have_keyboard && seat->wl_keyboard == NULL) {
+ seat->wl_keyboard = wl_seat_get_keyboard(wl_seat);
+ wl_keyboard_add_listener(seat->wl_keyboard,
+ &wl_keyboard_listener, seat);
+//fprintf(stderr, "wl_keyboard version=%d\n", wl_keyboard_get_version(seat->wl_keyboard));
+
+ } else if (!have_keyboard && seat->wl_keyboard != NULL) {
+ wl_keyboard_release(seat->wl_keyboard);
+ seat->wl_keyboard = NULL;
+ }
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (scr_driver->text_input_base) {
+ seat->text_input = zwp_text_input_manager_v3_get_text_input(scr_driver->text_input_base, seat->wl_seat);
+//printf("seat->text_input=%p\n",seat->text_input);
+ zwp_text_input_v3_add_listener(seat->text_input, &text_input_listener, NULL);
+ }
+}
+
+static void seat_name(void *data, struct wl_seat *wl_seat, const char *name) {
+ struct seat *seat = (struct seat*)data;
+ seat->name = strdup(name);
+}
+
+static struct wl_seat_listener seat_listener = {
+ seat_capabilities,
+ seat_name
+};
+
+static void output_geometry(void *data,
+ struct wl_output *wl_output,
+ int32_t x,
+ int32_t y,
+ int32_t physical_width,
+ int32_t physical_height,
+ int32_t subpixel,
+ const char *make,
+ const char *model,
+ int32_t transform)
+{
+ //fprintf(stderr, "output_geometry: x=%d y=%d physical=%dx%d\n",x,y,physical_width,physical_height);
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ output->dpi = 96; // to elaborate
+}
+
+static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
+ int32_t width, int32_t height, int32_t refresh)
+{
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ output->x_org = 0;
+ output->y_org = 0;
+ output->width = width;
+ output->height = height;
+//fprintf(stderr, "output_mode: [%p]=%dx%d\n",output->wl_output,width,height);
+}
+
+static void output_done(void *data, struct wl_output *wl_output)
+{
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ Fl_Wayland_Window_Driver::window_output *window_output;
+ struct seat *seat;
+//fprintf(stderr, "output_done output=%p\n",output);
+ Fl_X *xp = Fl_X::first;
+ while (xp) { // all mapped windows
+ struct wld_window *win = xp->xid;
+ wl_list_for_each(window_output, &(win->outputs), link) { // all Fl_Wayland_Window_Driver::window_output for this window
+ if (window_output->output == output) {
+ Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(win->fl_win);
+ if (output->wld_scale != win->scale) win_driver->update_scale();
+ }
+ }
+ xp = xp->next;
+ }
+
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ wl_list_for_each(seat, &(scr_driver->seats), link) {
+ try_update_cursor(seat);
+ }
+ scr_driver->init_workarea();
+ Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL);
+}
+
+
+static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) {
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ output->wld_scale = factor;
+//fprintf(stderr,"output_scale: wl_output=%p factor=%d\n",wl_output, factor);
+}
+
+
+static struct wl_output_listener output_listener = {
+ output_geometry,
+ output_mode,
+ output_done,
+ output_scale
+};
+
+
+static void registry_handle_global(void *user_data, struct wl_registry *wl_registry,
+ uint32_t id, const char *interface, uint32_t version) {
+//fprintf(stderr, "interface=%s\n", interface);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (strcmp(interface, "wl_compositor") == 0) {
+ if (version < 4) {
+ Fl::fatal("wl_compositor version >= 4 required");
+ }
+ scr_driver->wl_compositor = (struct wl_compositor*)wl_registry_bind(wl_registry,
+ id, &wl_compositor_interface, 4);
+
+ } else if (strcmp(interface, "wl_subcompositor") == 0) {
+ scr_driver->wl_subcompositor = (struct wl_subcompositor*)wl_registry_bind(wl_registry,
+ id, &wl_subcompositor_interface, 1);
+
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ scr_driver->wl_shm = (struct wl_shm*)wl_registry_bind(wl_registry,
+ id, &wl_shm_interface, 1);
+ wl_shm_add_listener(scr_driver->wl_shm, &shm_listener, NULL);
+
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ if (version < 3) {
+ Fl::fatal("%s version 3 required but only version %i is available\n", interface, version);
+ }
+ if (!scr_driver->seat) scr_driver->seat = (struct seat*)calloc(1, sizeof(struct seat));
+//fprintf(stderr, "registry_handle_global: seat=%p\n", scr_driver->seat);
+ wl_list_init(&scr_driver->seat->pointer_outputs);
+ scr_driver->seat->wl_seat = (wl_seat*)wl_registry_bind(wl_registry, id, &wl_seat_interface, 3);
+ scr_driver->seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ const char *locale = getenv("LC_ALL");
+ if (!locale || !*locale)
+ locale = getenv("LC_CTYPE");
+ if (!locale || !*locale)
+ locale = getenv("LANG");
+ if (!locale || !*locale)
+ locale = "C";
+ struct xkb_compose_table *table = xkb_compose_table_new_from_locale(scr_driver->seat->xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
+ scr_driver->seat->xkb_compose_state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
+ wl_seat_add_listener(scr_driver->seat->wl_seat, &seat_listener, scr_driver->seat);
+ if (scr_driver->seat->data_device_manager) {
+ scr_driver->seat->data_device = wl_data_device_manager_get_data_device(scr_driver->seat->data_device_manager, scr_driver->seat->wl_seat);
+ wl_data_device_add_listener(scr_driver->seat->data_device, Fl_Wayland_Screen_Driver::p_data_device_listener, NULL);
+ }
+
+ } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
+ if (!scr_driver->seat) scr_driver->seat = (struct seat*)calloc(1, sizeof(struct seat));
+ scr_driver->seat->data_device_manager = (struct wl_data_device_manager*)wl_registry_bind(wl_registry, id, &wl_data_device_manager_interface, 3);
+ if (scr_driver->seat->wl_seat) {
+ scr_driver->seat->data_device = wl_data_device_manager_get_data_device(scr_driver->seat->data_device_manager, scr_driver->seat->wl_seat);
+ wl_data_device_add_listener(scr_driver->seat->data_device, Fl_Wayland_Screen_Driver::p_data_device_listener, NULL);
+ }
+//fprintf(stderr, "registry_handle_global: %s\n", interface);
+
+ } else if (strcmp(interface, "wl_output") == 0) {
+ if (version < 2) {
+ Fl::fatal("%s version 3 required but only version %i is available\n", interface, version);
+ }
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)calloc(1, sizeof *output);
+ output->id = id;
+ output->wld_scale = 1;
+ output->wl_output = (struct wl_output*)wl_registry_bind(wl_registry,
+ id, &wl_output_interface, 2);
+ output->gui_scale = 1.f;
+ wl_proxy_set_tag((struct wl_proxy *) output->wl_output, &proxy_tag);
+ wl_output_add_listener(output->wl_output, &output_listener, output);
+ wl_list_insert(&(scr_driver->outputs), &output->link);
+ scr_driver->screen_count( wl_list_length(&(scr_driver->outputs)) );
+//fprintf(stderr, "wl_output: id=%d wl_output=%p screen_count()=%d\n", id, output->wl_output, Fl::screen_count());
+
+ } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
+//fprintf(stderr, "registry_handle_global interface=%s\n", interface);
+ scr_driver->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, id, &xdg_wm_base_interface, 1);
+ xdg_wm_base_add_listener(scr_driver->xdg_wm_base, &xdg_wm_base_listener, NULL);
+ } else if (strcmp(interface, "gtk_shell1") == 0) {
+ Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::MUTTER;
+ //fprintf(stderr, "Running the Mutter compositor\n");
+ } else if (strcmp(interface, "weston_desktop_shell") == 0) {
+ Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::WESTON;
+ //fprintf(stderr, "Running the Weston compositor\n");
+ } else if (strcmp(interface, "org_kde_plasma_shell") == 0) {
+ Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::KDE;
+ //fprintf(stderr, "Running the KDE compositor\n");
+ }
+ else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
+ scr_driver->text_input_base = (struct zwp_text_input_manager_v3 *) wl_registry_bind(wl_registry, id, &zwp_text_input_manager_v3_interface, 1);
+ //printf("scr_driver->text_input_base=%p version=%d\n",scr_driver->text_input_base,version);
+ }
+}
+
+
+static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
+{//TODO to be tested
+ Fl_Wayland_Screen_Driver::output *output;
+ Fl_Wayland_Window_Driver::window_output *window_output, *tmp;
+//fprintf(stderr, "registry_handle_global_remove data=%p id=%u\n", data, name);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ wl_list_for_each(output, &(scr_driver->outputs), link) { // all screens of the system
+ if (output->id == name) { // the screen being removed
+ Fl_X *xp = Fl_X::first;
+ while (xp) { // all mapped windows
+ struct wld_window *win = xp->xid;
+ wl_list_for_each_safe(window_output, tmp, &(win->outputs), link) { // all Fl_Wayland_Window_Driver::window_output for this window
+ if (window_output->output == output) {
+ wl_list_remove(&window_output->link);
+ free(window_output);
+ }
+ }
+ xp = xp->next;
+ }
+ wl_list_remove(&output->link);
+ scr_driver->screen_count( wl_list_length(&(scr_driver->outputs)) );
+ wl_output_destroy(output->wl_output);
+ free(output);
+ break;
+ }
+ }
+}
+
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+
+static void fd_callback(int unused, struct wl_display *display) {
+ wl_display_dispatch(display);
+}
+
+
+Fl_Wayland_Screen_Driver::Fl_Wayland_Screen_Driver() : Fl_Screen_Driver() {
+ libdecor_context = NULL;
+ seat = NULL;
+ text_input_base = NULL;
+ reset_cursor();
+}
+
+void Fl_Wayland_Screen_Driver::open_display_platform() {
+ struct wl_display *wl_display;
+ struct wl_registry *wl_registry;
+
+ static bool beenHereDoneThat = false;
+ if (beenHereDoneThat)
+ return;
+
+ beenHereDoneThat = true;
+ wl_display = wl_display_connect(NULL);
+ if (!wl_display) {
+ Fl::fatal("No Wayland connection\n");
+ }
+ fl_display = wl_display;
+ wl_list_init(&seats);
+ wl_list_init(&outputs);
+
+ wl_registry = wl_display_get_registry(wl_display);
+ wl_registry_add_listener(wl_registry, &registry_listener, NULL);
+ wl_display_dispatch(wl_display);
+ wl_display_roundtrip(wl_display);
+ if (!has_xrgb) {
+ Fl::fatal("Error: no WL_SHM_FORMAT_ARGB8888 shm format\n");
+ }
+ if (compositor == Fl_Wayland_Screen_Driver::unspecified) {
+ Fl::warning("FLTK could not identify the type of the running Wayland compositor");
+ }
+ Fl::add_fd(wl_display_get_fd(wl_display), FL_READ, (Fl_FD_Handler)fd_callback, wl_display);
+ fl_create_print_window();
+}
+
+void Fl_Wayland_Screen_Driver::close_display() {
+ Fl::remove_fd(wl_display_get_fd(fl_display));
+ wl_display_disconnect(fl_display);
+}
+
+
+static int workarea_xywh[4] = { -1, -1, -1, -1 };
+
+
+void Fl_Wayland_Screen_Driver::init_workarea()
+{
+ workarea_xywh[0] = 0;
+ workarea_xywh[1] = 0;
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ workarea_xywh[2] = output->width; // pixels
+ workarea_xywh[3] = output->height; // pixels
+ break;
+ }
+}
+
+
+int Fl_Wayland_Screen_Driver::x() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[0] / (output->gui_scale * output->wld_scale);
+}
+
+int Fl_Wayland_Screen_Driver::y() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[1] / (output->gui_scale * output->wld_scale);
+}
+
+int Fl_Wayland_Screen_Driver::w() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[2] / (output->gui_scale * output->wld_scale);
+}
+
+int Fl_Wayland_Screen_Driver::h() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[3] / (output->gui_scale * output->wld_scale);
+}
+
+
+void Fl_Wayland_Screen_Driver::init() {
+ if (!fl_display) open_display();
+}
+
+
+void Fl_Wayland_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n)
+{
+ if (num_screens < 0) init();
+ if (n < 0 || n >= num_screens) n = 0;
+ if (n == 0) { // for the main screen, these return the work area
+ X = Fl::x();
+ Y = Fl::y();
+ W = Fl::w();
+ H = Fl::h();
+ } else { // for other screens, work area is full screen,
+ screen_xywh(X, Y, W, H, n);
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n)
+{
+ if (num_screens < 0) init();
+
+ if ((n < 0) || (n >= num_screens))
+ n = 0;
+
+ if (num_screens > 0) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) { // n'th screen of the system
+ float s = output->gui_scale * output->wld_scale;
+ X = output->x_org / s;
+ Y = output->y_org / s;
+ W = output->width / s;
+ H = output->height / s;
+ break;
+ }
+ }
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::screen_dpi(float &h, float &v, int n)
+{
+ if (num_screens < 0) init();
+ h = v = 0.0f;
+
+ if (n >= 0 && n < num_screens) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) { // n'th screen of the system
+ h = output->dpi;
+ v = output->dpi;
+ break;
+ }
+ }
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::beep(int type)
+{
+ fprintf(stderr, "\007");
+}
+
+
+void Fl_Wayland_Screen_Driver::flush()
+{
+ if (fl_display) {
+ wl_display_flush(fl_display);
+ }
+}
+
+
+extern void fl_fix_focus(); // in Fl.cxx
+
+
+void Fl_Wayland_Screen_Driver::grab(Fl_Window* win)
+{
+ Fl_Window *fullscreen_win = NULL;
+ for (Fl_Window *W = Fl::first_window(); W; W = Fl::next_window(W)) {
+ if (W->fullscreen_active()) {
+ fullscreen_win = W;
+ break;
+ }
+ }
+ if (win) {
+ if (!Fl::grab()) {
+ }
+ Fl::grab_ = win; // FIXME: Fl::grab_ "should be private", but we need
+ // a way to *set* the variable from the driver!
+ } else {
+ if (Fl::grab()) {
+ // We must keep the grab in the non-EWMH fullscreen case
+ if (!fullscreen_win ) {
+ //XUngrabKeyboard(fl_display, fl_event_time);
+ }
+ //XUngrabPointer(fl_display, fl_event_time);
+ // this flush is done in case the picked menu item goes into
+ // an infinite loop, so we don't leave the X server locked up:
+ //XFlush(fl_display);
+ Fl::grab_ = 0; // FIXME: Fl::grab_ "should be private", but we need
+ // a way to *set* the variable from the driver!
+ fl_fix_focus();
+ }
+ }
+}
+
+
+static void set_selection_color(uchar r, uchar g, uchar b)
+{
+ Fl::set_color(FL_SELECTION_COLOR,r,g,b);
+}
+
+static void getsyscolor(const char *key1, const char* key2, const char *arg, const char *defarg, void (*func)(uchar,uchar,uchar))
+{
+ uchar r, g, b;
+ if (!arg) arg = defarg;
+ if (!Fl::screen_driver()->parse_color(arg, r, g, b))
+ Fl::error("Unknown color: %s", arg);
+ else
+ func(r, g, b);
+}
+
+
+void Fl_Wayland_Screen_Driver::get_system_colors()
+{
+ open_display();
+ const char* key1 = 0;
+ if (Fl::first_window()) key1 = Fl::first_window()->xclass();
+ if (!key1) key1 = "fltk";
+ if (!bg2_set)
+ getsyscolor("Text","background", fl_bg2, "#ffffff", Fl::background2);
+ if (!fg_set)
+ getsyscolor(key1, "foreground", fl_fg, "#000000", Fl::foreground);
+ if (!bg_set)
+ getsyscolor(key1, "background", fl_bg, "#c0c0c0", Fl::background);
+ getsyscolor("Text", "selectBackground", 0, "#000080", set_selection_color);
+}
+
+
+const char *Fl_Wayland_Screen_Driver::get_system_scheme()
+{
+ return getenv("FLTK_SCHEME");
+}
+
+
+Fl_RGB_Image *Fl_Wayland_Screen_Driver::read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win,
+ bool ignore, bool *p_ignore) {
+ Window xid = win ? fl_xid(win) : NULL;
+ struct fl_wld_buffer *buffer = win ? xid->buffer : (Fl_Offscreen)Fl_Surface_Device::surface()->driver()->gc();
+ float s = win ? xid->scale * scale(win->screen_num()) :
+ Fl_Surface_Device::surface()->driver()->scale();
+ int Xs, Ys, ws, hs;
+ if (s == 1) {
+ Xs = X; Ys = Y; ws = w; hs = h;
+ } else {
+ Xs = Fl_Scalable_Graphics_Driver::floor(X, s);
+ Ys = Fl_Scalable_Graphics_Driver::floor(Y, s);
+ ws = Fl_Scalable_Graphics_Driver::floor(X+w, s) - Xs;
+ hs = Fl_Scalable_Graphics_Driver::floor(Y+h, s) - Ys;
+ }
+ if (ws == 0 || hs == 0) return NULL;
+ uchar *data = new uchar[ws * hs * 3];
+ uchar *p = data, *q;
+ for (int j = 0; j < hs; j++) {
+ q = buffer->draw_buffer + (j+Ys) * buffer->stride + 4 * Xs;
+ for (int i = 0; i < ws; i++) {
+ *p++ = *(q+2); // R
+ *p++ = *(q+1); // G
+ *p++ = *q; // B
+ q += 4;
+ }
+ }
+ Fl_RGB_Image *rgb = new Fl_RGB_Image(data, ws, hs, 3);
+ rgb->alloc_array = 1;
+ return rgb;
+}
+
+
+void Fl_Wayland_Screen_Driver::offscreen_size(Fl_Offscreen off, int &width, int &height)
+{
+ width = off->width;
+ height = off->data_size / off->stride;
+}
+
+//NOTICE: returns -1 if x,y is not in any screen
+int Fl_Wayland_Screen_Driver::screen_num_unscaled(int x, int y)
+{
+ if (num_screens < 0) init();
+
+ Fl_Wayland_Screen_Driver::output *output;
+ int screen = 0;
+ wl_list_for_each(output, &outputs, link) {
+ int s = output->wld_scale;
+ int sx = output->x_org/s, sy = output->y_org/s, sw = output->width/s, sh = output->height/s;
+ if ((x >= sx) && (x < (sx+sw)) && (y >= sy) && (y < (sy+sh))) {
+ return screen;
+ }
+ screen++;
+ }
+ return -1;
+}
+
+float Fl_Wayland_Screen_Driver::scale(int n) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) break;
+ }
+ return output->gui_scale;
+}
+
+
+void Fl_Wayland_Screen_Driver::scale(int n, float f) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) {
+ output->gui_scale = f;
+ return;
+ }
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::set_cursor() {
+ do_set_cursor(seat);
+}
+
+struct wl_cursor *Fl_Wayland_Screen_Driver::default_cursor() {
+ return seat->default_cursor;
+}
+
+void Fl_Wayland_Screen_Driver::default_cursor(struct wl_cursor *cursor) {
+ seat->default_cursor = cursor;
+}
+
+struct wl_cursor *Fl_Wayland_Screen_Driver::cache_cursor(const char *cursor_name) {
+ return wl_cursor_theme_get_cursor(seat->cursor_theme, cursor_name);
+}
+
+void Fl_Wayland_Screen_Driver::reset_cursor() {
+ xc_arrow = xc_ns = xc_wait = xc_insert = xc_hand = xc_help = xc_cross = xc_move = xc_north = xc_south = xc_west = xc_east = xc_we = xc_nesw = xc_nwse = xc_sw = xc_se = xc_ne = xc_nw = NULL;
+}
+
+uint32_t Fl_Wayland_Screen_Driver::get_serial() {
+ return seat->serial;
+}
+
+struct wl_seat*Fl_Wayland_Screen_Driver::get_wl_seat() {
+ return seat->wl_seat;
+}
+
+char *Fl_Wayland_Screen_Driver::get_seat_name() {
+ return seat->name;
+}
+
+struct xkb_keymap *Fl_Wayland_Screen_Driver::get_xkb_keymap() {
+ return seat->xkb_keymap;
+}