diff options
| author | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
|---|---|---|
| committer | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
| commit | db214d1145e46d527a46d1fc2519548d2c4d23f1 (patch) | |
| tree | cf0fd9922e4d54f6beb63888f9b28c8e2a787bdf /src/drivers/Wayland | |
| parent | 75fc94d6c71fe686f6dde5b41ad91cba2f6bdd6f (diff) | |
wip: fork
Diffstat (limited to 'src/drivers/Wayland')
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H | 34 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx | 60 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H | 62 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx | 486 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H | 72 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx | 310 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H | 40 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx | 185 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Screen_Driver.H | 193 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx | 2204 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Window_Driver.H | 185 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx | 2191 | ||||
| -rw-r--r-- | src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx | 741 | ||||
| -rw-r--r-- | src/drivers/Wayland/fl_wayland_platform_init.cxx | 157 |
14 files changed, 0 insertions, 6920 deletions
diff --git a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H b/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H deleted file mode 100644 index e10a801ce..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK). -// -// Copyright 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 -// - -#ifndef FL_WAYLAND_COPY_SURFACE_DRIVER_H -#define FL_WAYLAND_COPY_SURFACE_DRIVER_H - -#include <FL/Fl_Copy_Surface.H> -#include <FL/Fl_Image_Surface.H> - -class Fl_Wayland_Copy_Surface_Driver : public Fl_Copy_Surface_Driver { - friend class Fl_Copy_Surface_Driver; - Fl_Image_Surface *img_surf; -protected: - Fl_Wayland_Copy_Surface_Driver(int w, int h); - ~Fl_Wayland_Copy_Surface_Driver(); - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; -}; - -#endif // FL_WAYLAND_COPY_SURFACE_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx deleted file mode 100644 index 043114781..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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 "Fl_Wayland_Copy_Surface_Driver.H" -#include <FL/Fl_Image_Surface.H> -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Window_Driver.H" - - -Fl_Wayland_Copy_Surface_Driver::Fl_Wayland_Copy_Surface_Driver(int w, int h) : Fl_Copy_Surface_Driver(w, h) { - float os_scale = Fl_Graphics_Driver::default_driver().scale(); - int d = 1; - if (Fl::first_window()) { - d = Fl_Wayland_Window_Driver::driver(Fl::first_window())->wld_scale(); - } - img_surf = new Fl_Image_Surface(int(w * os_scale) * d, int(h * os_scale) * d); - driver(img_surf->driver()); - driver()->scale(d * os_scale); -} - - -Fl_Wayland_Copy_Surface_Driver::~Fl_Wayland_Copy_Surface_Driver() { - Fl_RGB_Image *rgb = img_surf->image(); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - scr_driver->copy_image(rgb->array, rgb->data_w(), rgb->data_h()); - delete rgb; - delete img_surf; - driver(NULL); -} - - -void Fl_Wayland_Copy_Surface_Driver::set_current() { - Fl_Surface_Device::set_current(); - Fl_Cairo_Graphics_Driver *dr = (Fl_Cairo_Graphics_Driver*)driver(); - if (!dr->cr()) dr->set_cairo((cairo_t*)img_surf->offscreen()); -} - - -void Fl_Wayland_Copy_Surface_Driver::translate(int x, int y) { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_translate(x, y); -} - - -void Fl_Wayland_Copy_Surface_Driver::untranslate() { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_untranslate(); -} diff --git a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H b/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H deleted file mode 100644 index ad67c01bf..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H +++ /dev/null @@ -1,62 +0,0 @@ -// -// Class Fl_Wayland_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021-2023 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 -// - -#ifndef FL_WAYLAND_GL_WINDOW_DRIVER_H -#define FL_WAYLAND_GL_WINDOW_DRIVER_H - -#include <config.h> -#if HAVE_GL -#include "../../Fl_Gl_Window_Driver.H" -#include <wayland-egl.h> -#include <EGL/egl.h> -#include <FL/gl.h> - - -class Fl_Wayland_Gl_Window_Driver : public Fl_Gl_Window_Driver { - friend Fl_Gl_Window_Driver* Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *); - friend class Fl_Wayland_Gl_Plugin; -private: - static EGLDisplay egl_display; - struct wl_egl_window *egl_window; - EGLSurface egl_surface; - bool need_swap; - Fl_Wayland_Gl_Window_Driver(Fl_Gl_Window *win); - float pixels_per_unit() FL_OVERRIDE; - void make_current_before() FL_OVERRIDE; - int mode_(int m, const int *a) FL_OVERRIDE; - void swap_buffers() FL_OVERRIDE; - void resize(int is_a_resize, int w, int h) FL_OVERRIDE; - char swap_type() FL_OVERRIDE; - void swap_interval(int) FL_OVERRIDE; - int swap_interval() const FL_OVERRIDE; - Fl_Gl_Choice *find(int m, const int *alistp) FL_OVERRIDE; - GLContext create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g) FL_OVERRIDE; - void set_gl_context(Fl_Window* w, GLContext context) FL_OVERRIDE; - void delete_gl_context(GLContext) FL_OVERRIDE; - void make_overlay_current() FL_OVERRIDE; - void redraw_overlay() FL_OVERRIDE; - void gl_start() FL_OVERRIDE; - void gl_visual(Fl_Gl_Choice *c) FL_OVERRIDE; - void init(); - void* GetProcAddress(const char *procName) FL_OVERRIDE; -public: - static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time); - //virtual bool need_scissor() { return true; } // CONTROL_LEAKING_SUB_GL_WINDOWS - //void apply_scissor(); // CONTROL_LEAKING_SUB_GL_WINDOWS -}; - -#endif // HAVE_GL -#endif // FL_WAYLAND_GL_WINDOW_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx deleted file mode 100644 index d20b941b7..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx +++ /dev/null @@ -1,486 +0,0 @@ -// -// Class Fl_Wayland_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021-2023 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> -#if HAVE_GL -#include <FL/platform.H> -#include <FL/Fl_Image_Surface.H> -#include "../../Fl_Gl_Choice.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Gl_Window_Driver.H" -#include "../Posix/Fl_Posix_System_Driver.H" -#ifdef FLTK_USE_X11 -# include "../X11/Fl_X11_Gl_Window_Driver.H" -#endif -#include <wayland-egl.h> -#include <EGL/egl.h> -#include <FL/gl.h> - -/* Implementation notes about OpenGL drawing on the Wayland platform - -* After eglCreateWindowSurface() with attributes {EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER, EGL_NONE}, -eglQueryContext() reports that EGL_RENDER_BUFFER equals EGL_BACK_BUFFER. -This experiment suggests that the platform only supports double-buffer drawing. -Consequently, FL_DOUBLE is enforced in all Fl_Gl_Window::mode_ values under Wayland. - -* Commented out code marked with CONTROL_LEAKING_SUB_GL_WINDOWS aims to prevent - sub GL windows from leaking out from their parent by making leaking parts fully transparent. - This code is commented out because it requires the FL_ALPHA flag to be on - which not all client applications do. -*/ - -// Describes crap needed to create a GLContext. -class Fl_Wayland_Gl_Choice : public Fl_Gl_Choice { - friend class Fl_Wayland_Gl_Window_Driver; -private: - EGLConfig egl_conf; -public: - Fl_Wayland_Gl_Choice(int m, const int *alistp, Fl_Gl_Choice *n) : Fl_Gl_Choice(m, alistp, n) { - egl_conf = 0; - } -}; - - -struct gl_start_support { // to support use of gl_start / gl_finish - struct wl_surface *surface; - struct wl_subsurface *subsurface; - struct wl_egl_window *egl_window; - EGLSurface egl_surface; -}; - - -static EGLConfig wld_egl_conf = NULL; -static EGLint swap_interval_ = 1; -static EGLint max_swap_interval = 1000; -static EGLint min_swap_interval = 0; - - -EGLDisplay Fl_Wayland_Gl_Window_Driver::egl_display = EGL_NO_DISPLAY; - - -Fl_Wayland_Gl_Window_Driver::Fl_Wayland_Gl_Window_Driver(Fl_Gl_Window *win) : - Fl_Gl_Window_Driver(win) { - if (egl_display == EGL_NO_DISPLAY) init(); - egl_window = NULL; - egl_surface = NULL; - need_swap = false; -} - - -Fl_Gl_Window_Driver *Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *w) -{ -#ifdef FLTK_USE_X11 - if (!Fl_Wayland_Screen_Driver::wl_display) return new Fl_X11_Gl_Window_Driver(w); -#endif - return new Fl_Wayland_Gl_Window_Driver(w); -} - - -void Fl_Wayland_Gl_Window_Driver::init() { - EGLint major, minor; - - if (!fl_wl_display()) fl_open_display(); - egl_display = eglGetDisplay((EGLNativeDisplayType) fl_wl_display()); - if (egl_display == EGL_NO_DISPLAY) { - Fl::fatal("Can't create egl display\n"); - } - - if (eglInitialize(egl_display, &major, &minor) != EGL_TRUE) { - Fl::fatal("Can't initialise egl display\n"); - } - //printf("EGL major: %d, minor %d\n", major, minor); - //eglGetConfigs(egl_display, NULL, 0, &configs_count); - //printf("EGL has %d configs\n", configs_count); - eglBindAPI(EGL_OPENGL_API); -} - - -Fl_Gl_Choice *Fl_Wayland_Gl_Window_Driver::find(int m, const int *alistp) -{ - m |= FL_DOUBLE; - //if (pWindow->parent()) m |= FL_ALPHA; // CONTROL_LEAKING_SUB_GL_WINDOWS - Fl_Wayland_Gl_Choice *g = (Fl_Wayland_Gl_Choice*)Fl_Gl_Window_Driver::find_begin( - m, alistp); - if (g) return g; - - EGLint n; - EGLint config_attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_DEPTH_SIZE, 0, // set at 11 - EGL_SAMPLE_BUFFERS, 0, // set at 13 - EGL_STENCIL_SIZE, 0, // set at 15 - EGL_ALPHA_SIZE, 0, // set at 17 - EGL_NONE - }; - - if (m & FL_DEPTH32) - config_attribs[11] = 32; // request at least 32 bits - else if (m & FL_DEPTH) - config_attribs[11] = 1; // accept any size - - if (m & FL_MULTISAMPLE) config_attribs[13] = 1; - if (m & FL_STENCIL) config_attribs[15] = 1; - if (m & FL_ALPHA) config_attribs[17] = (m & FL_RGB8) ? 8 : 1; - - g = new Fl_Wayland_Gl_Choice(m, alistp, first); - eglChooseConfig(egl_display, config_attribs, &(g->egl_conf), 1, &n); - if (n == 0 && (m & FL_MULTISAMPLE)) { - config_attribs[13] = 0; - eglChooseConfig(egl_display, config_attribs, &(g->egl_conf), 1, &n); - } - if (n == 0) { - Fl::fatal("failed to choose an EGL config\n"); - } - - eglGetConfigAttrib(egl_display, g->egl_conf, EGL_MAX_SWAP_INTERVAL, &max_swap_interval); - eglGetConfigAttrib(egl_display, g->egl_conf, EGL_MIN_SWAP_INTERVAL, &min_swap_interval); - - first = g; - return g; -} - - -GLContext Fl_Wayland_Gl_Window_Driver::create_gl_context(Fl_Window* window, - const Fl_Gl_Choice* g) { - GLContext shared_ctx = 0; - if (context_list && nContext) shared_ctx = context_list[0]; - - static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - GLContext ctx = (GLContext)eglCreateContext(egl_display, - ((Fl_Wayland_Gl_Choice*)g)->egl_conf, - (shared_ctx ? (EGLContext)shared_ctx : EGL_NO_CONTEXT), - context_attribs); -//fprintf(stderr, "eglCreateContext=%p shared_ctx=%p\n", ctx, shared_ctx); - if (ctx) { - add_context(ctx); - /* CONTROL_LEAKING_SUB_GL_WINDOWS - if (egl_surface) { - eglMakeCurrent(egl_display, egl_surface, egl_surface, (EGLContext)ctx); - glClearColor(0., 0., 0., 1.); // set opaque black as starting background color - apply_scissor(); - }*/ - } - return ctx; -} - - -void Fl_Wayland_Gl_Window_Driver::set_gl_context(Fl_Window* w, GLContext context) { - struct wld_window *win = fl_wl_xid(w); - if (!win) return; - Fl_Wayland_Window_Driver *dr = Fl_Wayland_Window_Driver::driver(w); - EGLSurface target_egl_surface = NULL; - if (egl_surface) target_egl_surface = egl_surface; - else if (dr->gl_start_support_) target_egl_surface = dr->gl_start_support_->egl_surface; - if (!target_egl_surface) { // useful for gl_start() - dr->gl_start_support_ = new struct gl_start_support; - float s = Fl::screen_scale(w->screen_num()); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - // the GL scene will be a transparent subsurface above the cairo-drawn surface - dr->gl_start_support_->surface = - wl_compositor_create_surface(scr_driver->wl_compositor); - dr->gl_start_support_->subsurface = wl_subcompositor_get_subsurface( - scr_driver->wl_subcompositor, dr->gl_start_support_->surface, win->wl_surface); - wl_subsurface_set_position(dr->gl_start_support_->subsurface, w->x() * s, w->y() * s); - wl_subsurface_place_above(dr->gl_start_support_->subsurface, win->wl_surface); - dr->gl_start_support_->egl_window = wl_egl_window_create( - dr->gl_start_support_->surface, w->w() * s, w->h() * s); - target_egl_surface = dr->gl_start_support_->egl_surface = eglCreateWindowSurface( - egl_display, wld_egl_conf, dr->gl_start_support_->egl_window, NULL); - } - GLContext current_context = eglGetCurrentContext(); - if (context != current_context || w != cached_window) { - cached_window = w; - if (eglMakeCurrent(egl_display, target_egl_surface, target_egl_surface, - (EGLContext)context)) { -//fprintf(stderr, "EGLContext %p made current\n", context); - } else { - Fl::error("eglMakeCurrent() failed\n"); - } - } - if (!(mode() & FL_ALPHA)) { // useful at least for Linux on MacBook hardware - GLfloat vals[4]; - glGetFloatv(GL_COLOR_CLEAR_VALUE, vals); - if (vals[3] == 0.) glClearColor(vals[0], vals[1], vals[2], 1.); - } -} - -/* CONTROL_LEAKING_SUB_GL_WINDOWS -void Fl_Wayland_Gl_Window_Driver::apply_scissor() { - cairo_rectangle_int_t *extents = Fl_Wayland_Window_Driver::driver(pWindow)->subRect(); - if (extents) { - glDisable(GL_SCISSOR_TEST); - GLdouble vals[4]; - glGetDoublev(GL_COLOR_CLEAR_VALUE, vals); - glClearColor(0., 0., 0., 0.); - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(vals[0], vals[1], vals[2], vals[3]); - float s = pWindow->pixels_per_unit(); - glScissor(s*extents->x, s*extents->y, s*extents->width, s*extents->height); -//printf("apply_scissor %dx%d %dx%d\n",extents->x, extents->y, extents->width, extents->height); - glEnable(GL_SCISSOR_TEST); - } -}*/ - - -void Fl_Wayland_Gl_Window_Driver::delete_gl_context(GLContext context) { - GLContext current_context = eglGetCurrentContext(); - if (current_context == context) { - cached_window = 0; - } - if (current_context == (EGLContext)context) { - eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - eglDestroyContext(egl_display, (EGLContext)context); - eglDestroySurface(egl_display, egl_surface); - egl_surface = NULL; - wl_egl_window_destroy(egl_window); - egl_window = NULL; - del_context(context); -} - - -void Fl_Wayland_Gl_Window_Driver::make_overlay_current() { - glDrawBuffer(GL_FRONT); -} - - -void Fl_Wayland_Gl_Window_Driver::redraw_overlay() { - pWindow->redraw(); -} - - -void Fl_Wayland_Gl_Window_Driver::make_current_before() { - if (!egl_window) { - struct wld_window *win = fl_wl_xid(pWindow); - struct wl_surface *surface = win->wl_surface; - int W = pWindow->pixel_w(); - int H = pWindow->pixel_h(); - int scale = Fl_Wayland_Window_Driver::driver(pWindow)->wld_scale(); - egl_window = wl_egl_window_create(surface, (W/scale)*scale, (H/scale)*scale); - if (egl_window == EGL_NO_SURFACE) { - Fl::fatal("Can't create egl window with wl_egl_window_create()\n"); - } - Fl_Wayland_Gl_Choice *g = (Fl_Wayland_Gl_Choice*)this->g(); - egl_surface = eglCreateWindowSurface(egl_display, g->egl_conf, egl_window, NULL); - wl_surface_set_buffer_scale(surface, scale); - if (mode() & FL_ALPHA) wl_surface_set_opaque_region(surface, NULL); - // Tested apps: shape, glpuzzle, cube, fractals, gl_overlay, fullscreen, unittests, - // OpenGL3-glut-test, OpenGL3test. - // Tested wayland compositors: mutter, kde-plasma, weston, sway on FreeBSD. - if (pWindow->parent()) win = fl_wl_xid(pWindow->top_window()); - while (wl_list_empty(&win->outputs)) wl_display_dispatch(fl_wl_display()); - } -} - - -float Fl_Wayland_Gl_Window_Driver::pixels_per_unit() -{ - int ns = pWindow->screen_num(); - int wld_scale = (pWindow->shown() ? - Fl_Wayland_Window_Driver::driver(pWindow)->wld_scale() : 1); - return wld_scale * Fl::screen_driver()->scale(ns); -} - - -int Fl_Wayland_Gl_Window_Driver::mode_(int m, const int *a) { - mode(m | FL_DOUBLE); - return 1; -} - - -void Fl_Wayland_Gl_Window_Driver::surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - Fl_Wayland_Gl_Window_Driver *gl_dr = (Fl_Wayland_Gl_Window_Driver *)data; - wl_callback_destroy(cb); - struct wld_window *window = fl_wl_xid(gl_dr->pWindow); - window->frame_cb = NULL; - if (gl_dr->need_swap) { - eglSwapBuffers(Fl_Wayland_Gl_Window_Driver::egl_display, gl_dr->egl_surface); - gl_dr->need_swap = false; - } -} - - -static const struct wl_callback_listener surface_frame_listener = { - .done = Fl_Wayland_Gl_Window_Driver::surface_frame_done, -}; - - -void Fl_Wayland_Gl_Window_Driver::swap_buffers() { - if (overlay()) { - static bool overlay_buffer = true; - int wo = pWindow->pixel_w(), ho = pWindow->pixel_h(); - GLint matrixmode; - GLfloat pos[4]; - glGetIntegerv(GL_MATRIX_MODE, &matrixmode); - glGetFloatv(GL_CURRENT_RASTER_POSITION, pos); // save original glRasterPos - glMatrixMode(GL_PROJECTION); // save proj/model matrices - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glScalef(2.0f/wo, 2.0f/ho, 1.0f); - glTranslatef(-wo/2.0f, -ho/2.0f, 0.0f); // set transform so 0,0 is bottom/left of window - glRasterPos2i(0,0); // set glRasterPos to bottom left corner - { - // Emulate overlay by doing copypixels - glReadBuffer(overlay_buffer?GL_BACK:GL_FRONT); - glDrawBuffer(overlay_buffer?GL_FRONT:GL_BACK); - overlay_buffer = ! overlay_buffer; - glCopyPixels(0, 0, wo, ho, GL_COLOR); - } - glPopMatrix(); // GL_MODELVIEW // restore model/proj matrices - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(matrixmode); - glRasterPos3f(pos[0], pos[1], pos[2]); // restore original glRasterPos - if (!overlay_buffer) return; // don't call eglSwapBuffers until overlay has been drawn - } - - if (egl_surface) { - Fl_Window *parent = pWindow->parent() ? pWindow->window() : NULL; - struct wld_window *parent_xid = parent ? fl_wl_xid(parent) : NULL; - if (parent_xid) { // issue #967 - struct wld_window *xid = fl_wl_xid(pWindow); - if (xid->frame_cb) { - need_swap = true; - return; - } - if (!parent_xid->frame_cb) { - xid->frame_cb = wl_surface_frame(xid->wl_surface); - wl_callback_add_listener(xid->frame_cb, &surface_frame_listener, this); - } - } - eglSwapBuffers(Fl_Wayland_Gl_Window_Driver::egl_display, egl_surface); - need_swap = false; - } -} - - -class Fl_Wayland_Gl_Plugin : public Fl_Wayland_Plugin { -public: - Fl_Wayland_Gl_Plugin() : Fl_Wayland_Plugin(name()) { } - const char *name() FL_OVERRIDE { return "gl.wayland.fltk.org"; } - void do_swap(Fl_Window *w) FL_OVERRIDE { - Fl_Gl_Window_Driver *gldr = Fl_Gl_Window_Driver::driver(w->as_gl_window()); - if (gldr->overlay() == w) gldr->swap_buffers(); - } - void invalidate(Fl_Window *w) FL_OVERRIDE { - w->as_gl_window()->valid(0); - } - void terminate() FL_OVERRIDE { - if (Fl_Wayland_Gl_Window_Driver::egl_display != EGL_NO_DISPLAY) { - eglTerminate(Fl_Wayland_Gl_Window_Driver::egl_display); - } - } - void destroy(struct gl_start_support *gl_start_support_) FL_OVERRIDE { - eglDestroySurface(Fl_Wayland_Gl_Window_Driver::egl_display, - gl_start_support_->egl_surface); - wl_egl_window_destroy(gl_start_support_->egl_window); - wl_subsurface_destroy(gl_start_support_->subsurface); - wl_surface_destroy(gl_start_support_->surface); - delete gl_start_support_; - } -}; - - -static Fl_Wayland_Gl_Plugin Gl_Overlay_Plugin; - - -/* CONTROL_LEAKING_SUB_GL_WINDOWS -static void delayed_scissor(Fl_Wayland_Gl_Window_Driver *dr) { - dr->apply_scissor(); -}*/ - - -void Fl_Wayland_Gl_Window_Driver::resize(int is_a_resize, int W, int H) { - if (!egl_window) return; - float f = Fl::screen_scale(pWindow->screen_num()); - int s = Fl_Wayland_Window_Driver::driver(pWindow)->wld_scale(); - W = int(W * f) * s; // W, H must be multiples of int s - H = int(H * f) * s; - int W2, H2; - wl_egl_window_get_attached_size(egl_window, &W2, &H2); - if (W2 != W || H2 != H) { - struct wld_window *xid = fl_wl_xid(pWindow); - if (xid->kind == Fl_Wayland_Window_Driver::DECORATED && !xid->frame_cb) { - xid->frame_cb = wl_surface_frame(xid->wl_surface); - wl_callback_add_listener(xid->frame_cb, - Fl_Wayland_Graphics_Driver::p_surface_frame_listener, xid); - } - wl_egl_window_resize(egl_window, W, H, 0, 0); - wl_surface_set_buffer_scale(xid->wl_surface, s); - } - /* CONTROL_LEAKING_SUB_GL_WINDOWS - if (Fl_Wayland_Window_Driver::driver(pWindow)->subRect()) { - pWindow->redraw(); - Fl::add_timeout(0.01, (Fl_Timeout_Handler)delayed_scissor, this); - }*/ -} - - -char Fl_Wayland_Gl_Window_Driver::swap_type() { - return copy; -} - - -void Fl_Wayland_Gl_Window_Driver::gl_visual(Fl_Gl_Choice *c) { - Fl_Gl_Window_Driver::gl_visual(c); - wld_egl_conf = ((Fl_Wayland_Gl_Choice*)c)->egl_conf; -} - - -void Fl_Wayland_Gl_Window_Driver::gl_start() { - float f = Fl::screen_scale(Fl_Window::current()->screen_num()); - int W = Fl_Window::current()->w() * f; - int H = Fl_Window::current()->h() * f; - int W2, H2; - Fl_Wayland_Window_Driver *dr = Fl_Wayland_Window_Driver::driver(Fl_Window::current()); - wl_egl_window_get_attached_size(dr->gl_start_support_->egl_window, &W2, &H2); - if (W2 != W || H2 != H) { - wl_egl_window_resize(dr->gl_start_support_->egl_window, W, H, 0, 0); - } - glClearColor(0., 0., 0., 0.); - glClear(GL_COLOR_BUFFER_BIT); -} - -void Fl_Wayland_Gl_Window_Driver::swap_interval(int interval) { - if (interval < min_swap_interval) interval = min_swap_interval; - if (interval > max_swap_interval) interval = max_swap_interval; - if (egl_display && eglSwapInterval(egl_display, interval)) - swap_interval_ = interval; - // printf("swap_interval_=%d\n",swap_interval_); -} - - -int Fl_Wayland_Gl_Window_Driver::swap_interval() const { - return swap_interval_; -} - - -void* Fl_Wayland_Gl_Window_Driver::GetProcAddress(const char *procName) { - return Fl_Posix_System_Driver::dlopen_or_dlsym(NULL, procName); -} - - -FL_EXPORT EGLContext fl_wl_glcontext(GLContext rc) { return (EGLContext)rc; } - -#endif // HAVE_GL diff --git a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H b/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H deleted file mode 100644 index ac8786a47..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H +++ /dev/null @@ -1,72 +0,0 @@ -// -// Definition of class Fl_Wayland_Graphics_Driver. -// -// Copyright 2021-2023 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 -// - -/** - \file Fl_Wayland_Graphics_Driver.H - \brief Definition of Wayland graphics driver. - */ - -#ifndef FL_WAYLAND_GRAPHICS_DRIVER_H -#define FL_WAYLAND_GRAPHICS_DRIVER_H - -#include "../Cairo/Fl_Cairo_Graphics_Driver.H" -#include <stdint.h> // for uint32_t -#include <wayland-client.h> // for wl_list - - -class Fl_Wayland_Graphics_Driver : public Fl_Cairo_Graphics_Driver { -public: - struct draw_buffer { - unsigned char *buffer; - cairo_t *cairo_; - size_t data_size; // of wl_buffer and buffer - int stride; - int width; - }; - struct wld_buffer { - struct draw_buffer draw_buffer; - struct wl_list link; // links all buffers from the same wl_shm_pool - struct wl_buffer *wl_buffer; - void *data; - struct wl_shm_pool *shm_pool; - bool draw_buffer_needs_commit; - bool in_use; // true while being committed - bool released; // true after buffer_release() was called - }; - struct wld_shm_pool_data { // one record attached to each wl_shm_pool object - char *pool_memory; // start of mmap'ed memory encapsulated by the wl_shm_pool - size_t pool_size; // size of encapsulated memory - struct wl_list buffers; // to list of fl_wld_buffer's from this pool - }; - static const uint32_t wld_format; - static struct wl_shm_pool *current_pool; - static FL_EXPORT const struct wl_callback_listener *p_surface_frame_listener; - void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen osrc, - int srcx, int srcy) FL_OVERRIDE; - void cache_size(Fl_Image *img, int &width, int &height) FL_OVERRIDE; - static struct wld_buffer *create_wld_buffer(int width, int height, bool with_shm = true); - static void create_shm_buffer(wld_buffer *buffer); - static void buffer_release(struct wld_window *window); - static void buffer_commit(struct wld_window *window, cairo_region_t *r = NULL); - static void cairo_init(struct draw_buffer *buffer, int width, int height, int stride, - cairo_format_t format); - // used by class Fl_Wayland_Gl_Window_Driver - static FL_EXPORT struct draw_buffer *offscreen_buffer(Fl_Offscreen); - static const cairo_user_data_key_t key; - static Fl_Image_Surface *custom_offscreen(int w, int h, struct wld_buffer **buffer); -}; - -#endif // FL_WAYLAND_GRAPHICS_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx deleted file mode 100644 index 5c9539a8c..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx +++ /dev/null @@ -1,310 +0,0 @@ -// -// Implementation of the Wayland graphics driver. -// -// Copyright 2021-2023 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 "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include <FL/Fl_Image_Surface.H> -#include <sys/mman.h> -#include <unistd.h> // for close() -#include <errno.h> -#include <string.h> // for strerror() -#include <cairo/cairo.h> - -extern "C" { -# include "../../../libdecor/src/os-compatibility.h" // for libdecor_os_create_anonymous_file() -} - -// used by create_shm_buffer and do_buffer_release -struct wl_shm_pool *Fl_Wayland_Graphics_Driver::current_pool = NULL; - - -static void do_buffer_release(struct Fl_Wayland_Graphics_Driver::wld_buffer *); - - -static void buffer_release_listener(void *user_data, struct wl_buffer *wl_buffer) -{ - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer = - (struct Fl_Wayland_Graphics_Driver::wld_buffer*)user_data; - buffer->in_use = false; - if (buffer->released) do_buffer_release(buffer); -} - - -static const struct wl_buffer_listener buffer_listener = { - buffer_release_listener -}; - - -void Fl_Wayland_Graphics_Driver::create_shm_buffer(Fl_Wayland_Graphics_Driver::wld_buffer *buffer) { - int width = buffer->draw_buffer.width; - int stride = buffer->draw_buffer.stride; - int height = buffer->draw_buffer.data_size / stride; - const size_t default_pool_size = 10000000; // larger pools are possible if needed - int chunk_offset = 0; // offset to start of available memory in pool - struct wld_shm_pool_data *pool_data = current_pool ? // data record attached to current pool - (struct wld_shm_pool_data *)wl_shm_pool_get_user_data(current_pool) : NULL; - size_t pool_size = current_pool ? pool_data->pool_size : default_pool_size; // current pool size - if (current_pool && !wl_list_empty(&pool_data->buffers)) { - // last wld_buffer created from current pool - struct wld_buffer *record = wl_container_of(pool_data->buffers.next, record, link); - chunk_offset = ((char*)record->data - pool_data->pool_memory) + - record->draw_buffer.data_size; - } - if (!current_pool || chunk_offset + buffer->draw_buffer.data_size > pool_size) { - // if true, a new pool is needed - if (current_pool && wl_list_empty(&pool_data->buffers)) { - wl_shm_pool_destroy(current_pool); - /*int err = */munmap(pool_data->pool_memory, pool_data->pool_size); -// printf("create_shm_buffer munmap(%p)->%d\n", pool_data->pool_memory, err); - free(pool_data); - } - chunk_offset = 0; - pool_size = default_pool_size; - if (buffer->draw_buffer.data_size > pool_size) - pool_size = 2 * buffer->draw_buffer.data_size; // a larger pool is needed - int fd = libdecor_os_create_anonymous_file(pool_size); - if (fd < 0) { - Fl::fatal("libdecor_os_create_anonymous_file failed: %s\n", strerror(errno)); - } - pool_data = (struct wld_shm_pool_data*)calloc(1, sizeof(struct wld_shm_pool_data)); - pool_data->pool_memory = (char*)mmap(NULL, pool_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - if (pool_data->pool_memory == MAP_FAILED) { - close(fd); - Fl::fatal("mmap failed: %s\n", strerror(errno)); - } - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - current_pool = wl_shm_create_pool(scr_driver->wl_shm, fd, (int32_t)pool_size); - close(fd); // does not prevent the mmap'ed memory from being used - //printf("wl_shm_create_pool %p size=%lu\n",pool_data->pool_memory , pool_size); - pool_data->pool_size = pool_size; - wl_list_init(&pool_data->buffers); - wl_shm_pool_set_user_data(current_pool, pool_data); - } - buffer->wl_buffer = wl_shm_pool_create_buffer(current_pool, chunk_offset, - width, height, stride, wld_format); - wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer); - // add this buffer to head of list of current pool's buffers - wl_list_insert(&pool_data->buffers, &buffer->link); - buffer->shm_pool = current_pool; - buffer->data = (void*)(pool_data->pool_memory + chunk_offset); -//fprintf(stderr, "last=%p chunk_offset=%d ", pool_data->buffers.next, chunk_offset); -//fprintf(stderr, "create_shm_buffer: %dx%d = %d\n", width, height, size); -} - - -struct Fl_Wayland_Graphics_Driver::wld_buffer * - Fl_Wayland_Graphics_Driver::create_wld_buffer(int width, int height, bool with_shm) { - struct wld_buffer *buffer = (struct wld_buffer*)calloc(1, sizeof(struct wld_buffer)); - int stride = cairo_format_stride_for_width(cairo_format, width); - cairo_init(&buffer->draw_buffer, width, height, stride, cairo_format); - buffer->draw_buffer_needs_commit = true; - if (with_shm) create_shm_buffer(buffer); - return buffer; -} - - -// used to support both normal and progressive drawing and for top-level GL windows -static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - struct wld_window *window = (struct wld_window *)data; - wl_callback_destroy(cb); - window->frame_cb = NULL; - if (window->buffer && window->buffer->draw_buffer_needs_commit) { - Fl_Wayland_Graphics_Driver::buffer_commit(window); - } -} - - -static const struct wl_callback_listener surface_frame_listener = { - .done = surface_frame_done, -}; - - -const struct wl_callback_listener *Fl_Wayland_Graphics_Driver::p_surface_frame_listener = - &surface_frame_listener; - - -// copy pixels in region r from the Cairo surface to the Wayland buffer -static void copy_region(struct wld_window *window, cairo_region_t *r) { - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer = window->buffer; - float f = Fl::screen_scale(window->fl_win->screen_num()); - int d = Fl_Wayland_Window_Driver::driver(window->fl_win)->wld_scale(); - int count = cairo_region_num_rectangles(r); - cairo_rectangle_int_t rect; - for (int i = 0; i < count; i++) { - cairo_region_get_rectangle(r, i, &rect); - int left = d * int(rect.x * f); - int top = d * int(rect.y * f); - int right = d * ceil((rect.x + rect.width) * f); - if (right > d * int(window->fl_win->w() * f)) right = d * int(window->fl_win->w() * f); - int width = right - left; - int bottom = d * ceil((rect.y + rect.height) * f); - if (bottom > d * int(window->fl_win->h() * f)) bottom = d * int(window->fl_win->h() * f); - int height = bottom - top; - int offset = top * buffer->draw_buffer.stride + 4 * left; - int W4 = 4 * width; - for (int l = 0; l < height; l++) { - if (offset + W4 >= (int)buffer->draw_buffer.data_size) { - W4 = buffer->draw_buffer.data_size - offset; - if (W4 <= 0) break; - } - memcpy((uchar*)buffer->data + offset, buffer->draw_buffer.buffer + offset, W4); - offset += buffer->draw_buffer.stride; - } - wl_surface_damage_buffer(window->wl_surface, left, top, width, height); - } -} - - -void Fl_Wayland_Graphics_Driver::buffer_commit(struct wld_window *window, cairo_region_t *r) -{ - if (!window->buffer->wl_buffer) create_shm_buffer(window->buffer); - cairo_surface_t *surf = cairo_get_target(window->buffer->draw_buffer.cairo_); - cairo_surface_flush(surf); - if (r) copy_region(window, r); - else { - memcpy(window->buffer->data, window->buffer->draw_buffer.buffer, - window->buffer->draw_buffer.data_size); - wl_surface_damage_buffer(window->wl_surface, 0, 0, 1000000, 1000000); - } - window->buffer->in_use = true; - wl_surface_attach(window->wl_surface, window->buffer->wl_buffer, 0, 0); - wl_surface_set_buffer_scale( window->wl_surface, - Fl_Wayland_Window_Driver::driver(window->fl_win)->wld_scale() ); - if (!window->covered) { // see issue #878 - window->frame_cb = wl_surface_frame(window->wl_surface); - wl_callback_add_listener(window->frame_cb, p_surface_frame_listener, window); - } - wl_surface_commit(window->wl_surface); - window->buffer->draw_buffer_needs_commit = false; -} - - -void Fl_Wayland_Graphics_Driver::cairo_init(struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer, - int width, int height, int stride, - cairo_format_t format) { - buffer->data_size = stride * height; - buffer->stride = stride; - buffer->buffer = new uchar[buffer->data_size]; - buffer->width = width; - cairo_surface_t *surf = cairo_image_surface_create_for_data(buffer->buffer, format, - width, height, stride); - if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { - Fl::fatal("Can't create Cairo surface with cairo_image_surface_create_for_data()\n"); - return; - } - buffer->cairo_ = cairo_create(surf); - cairo_status_t err; - if ((err = cairo_status(buffer->cairo_)) != CAIRO_STATUS_SUCCESS) { - Fl::fatal("Cairo error during cairo_create() %s\n", cairo_status_to_string(err)); - return; - } - cairo_surface_destroy(surf); - memset(buffer->buffer, 0, buffer->data_size); // useful for transparent windows - cairo_set_source_rgba(buffer->cairo_, .0, .0, .0, 1.0); // Black default color - cairo_save(buffer->cairo_); -} - - -// runs when buffer->in_use is false and buffer->released is true -static void do_buffer_release(struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer) { - struct wl_shm_pool *my_pool = buffer->shm_pool; - if (buffer->wl_buffer) { - struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data *pool_data = - (struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data*) - wl_shm_pool_get_user_data(my_pool); - wl_buffer_destroy(buffer->wl_buffer); - // remove wld_buffer from list of pool's buffers - wl_list_remove(&buffer->link); - if (wl_list_empty(&pool_data->buffers) && my_pool != Fl_Wayland_Graphics_Driver::current_pool) { - // all buffers from pool are gone - wl_shm_pool_destroy(my_pool); - /*int err = */munmap(pool_data->pool_memory, pool_data->pool_size); - //printf("do_buffer_release munmap(%p)->%d\n", pool_data->pool_memory, err); - free(pool_data); - } - } - free(buffer); -} - - -void Fl_Wayland_Graphics_Driver::buffer_release(struct wld_window *window) -{ - if (window->buffer && !window->buffer->released) { - window->buffer->released = true; - if (window->frame_cb) { wl_callback_destroy(window->frame_cb); window->frame_cb = NULL; } - delete[] window->buffer->draw_buffer.buffer; - window->buffer->draw_buffer.buffer = NULL; - cairo_destroy(window->buffer->draw_buffer.cairo_); - if (!window->buffer->in_use) do_buffer_release(window->buffer); - window->buffer = NULL; - } -} - - -// this refers to the same memory layout for pixel data as does CAIRO_FORMAT_ARGB32 -const uint32_t Fl_Wayland_Graphics_Driver::wld_format = WL_SHM_FORMAT_ARGB8888; - - -void Fl_Wayland_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, - Fl_Offscreen src, int srcx, int srcy) { - // draw portion srcx,srcy,w,h of osrc to position x,y (top-left) of - // the graphics driver's surface - cairo_matrix_t matrix; - cairo_get_matrix(cairo_, &matrix); - double s = matrix.xx; - cairo_save(cairo_); - cairo_rectangle(cairo_, x - 0.5, y - 0.5, w, h); - cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE); - cairo_clip(cairo_); - cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT); - cairo_surface_t *surf = cairo_get_target((cairo_t *)src); - cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf); - cairo_set_source(cairo_, pat); - cairo_matrix_init_scale(&matrix, s, s); - cairo_matrix_translate(&matrix, -(x - srcx), -(y - srcy)); - cairo_pattern_set_matrix(pat, &matrix); - cairo_paint(cairo_); - cairo_pattern_destroy(pat); - cairo_restore(cairo_); - surface_needs_commit(); -} - - -const cairo_user_data_key_t Fl_Wayland_Graphics_Driver::key = {}; - - -struct Fl_Wayland_Graphics_Driver::draw_buffer* -Fl_Wayland_Graphics_Driver::offscreen_buffer(Fl_Offscreen offscreen) { - return (struct draw_buffer*)cairo_get_user_data((cairo_t*)offscreen, &key); -} - - -Fl_Image_Surface *Fl_Wayland_Graphics_Driver::custom_offscreen(int w, int h, - struct Fl_Wayland_Graphics_Driver::wld_buffer **p_off) { - struct wld_buffer *off = create_wld_buffer(w, h); - *p_off = off; - cairo_set_user_data(off->draw_buffer.cairo_, &key, &off->draw_buffer, NULL); - return new Fl_Image_Surface(w, h, 0, (Fl_Offscreen)off->draw_buffer.cairo_); -} - - -void Fl_Wayland_Graphics_Driver::cache_size(Fl_Image *img, int &width, int &height) { - Fl_Graphics_Driver::cache_size(img, width, height); - width *= wld_scale; - height *= wld_scale; -} diff --git a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H deleted file mode 100644 index ae32ac3df..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H +++ /dev/null @@ -1,40 +0,0 @@ -// -// Draw-to-image code for the Fast Light Tool Kit (FLTK). -// -// 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 -// - -#ifndef FL_WAYLAND_IMAGE_SURFACE_DRIVER_H -#define FL_WAYLAND_IMAGE_SURFACE_DRIVER_H - -#include <FL/Fl_Image_Surface.H> - -class Fl_Wayland_Image_Surface_Driver : public Fl_Image_Surface_Driver { - void end_current() FL_OVERRIDE; - struct wld_window *pre_window; -public: - Fl_Wayland_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off); - ~Fl_Wayland_Image_Surface_Driver(); - void mask(const Fl_RGB_Image *) FL_OVERRIDE; - struct shape_data_type { - double scale; - cairo_pattern_t *mask_pattern_; - cairo_t *bg_cr; - } *shape_data_; - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; - Fl_RGB_Image *image() FL_OVERRIDE; -}; - -#endif // FL_WAYLAND_IMAGE_SURFACE_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx deleted file mode 100644 index ec9c56cb7..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx +++ /dev/null @@ -1,185 +0,0 @@ -// -// Draw-to-image code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 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 <FL/platform.H> -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Image_Surface_Driver.H" - - -Fl_Wayland_Image_Surface_Driver::Fl_Wayland_Image_Surface_Driver(int w, int h, - int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) { - shape_data_ = NULL; - float s = 1; - int d = 1; - if (!off) { - fl_open_display(); - if (Fl::first_window()) { - d = Fl_Wayland_Window_Driver::driver(Fl::first_window())->wld_scale(); - } - s = Fl_Graphics_Driver::default_driver().scale(); - if (d*s != 1 && high_res) { - w = int(w * s) * d; - h = int(h * s) * d; - } - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - (struct Fl_Wayland_Graphics_Driver::draw_buffer*)calloc(1, - sizeof(struct Fl_Wayland_Graphics_Driver::draw_buffer)); - Fl_Wayland_Graphics_Driver::cairo_init(off_, w, h, - cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w), CAIRO_FORMAT_RGB24); - offscreen = (Fl_Offscreen)off_->cairo_; - cairo_set_user_data(off_->cairo_, &Fl_Wayland_Graphics_Driver::key, off_, NULL); - if (d*s != 1 && high_res) cairo_scale((cairo_t*)offscreen, d*s, d*s); - } - driver(new Fl_Wayland_Graphics_Driver()); - if (d*s != 1 && high_res) driver()->scale(d*s); -} - - -Fl_Wayland_Image_Surface_Driver::~Fl_Wayland_Image_Surface_Driver() { - if (shape_data_) { - cairo_surface_t *surf; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf); - unsigned char *bits = cairo_image_surface_get_data(surf); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] bits; - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - Fl_Wayland_Graphics_Driver::offscreen_buffer((Fl_Offscreen)shape_data_->bg_cr); - delete[] off_->buffer; - free(off_); - cairo_destroy(shape_data_->bg_cr); - free(shape_data_); - } - if (offscreen && !external_offscreen) { - struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer = - Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); - cairo_destroy((cairo_t *)offscreen); - delete[] buffer->buffer; - free(buffer); - } - delete driver(); -} - - -void Fl_Wayland_Image_Surface_Driver::set_current() { - Fl_Surface_Device::set_current(); - Fl_Cairo_Graphics_Driver *dr = (Fl_Cairo_Graphics_Driver*)driver(); - if (!dr->cr()) dr->set_cairo((cairo_t*)offscreen); - pre_window = Fl_Wayland_Window_Driver::wld_window; - Fl_Wayland_Window_Driver::wld_window = NULL; - fl_window = 0; -} - - -void Fl_Wayland_Image_Surface_Driver::end_current() { - cairo_surface_t *surf = cairo_get_target((cairo_t*)offscreen); - cairo_surface_flush(surf); - Fl_Wayland_Window_Driver::wld_window = pre_window; - fl_window = (Window)pre_window; - Fl_Surface_Device::end_current(); -} - - -void Fl_Wayland_Image_Surface_Driver::translate(int x, int y) { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_translate(x, y); -} - - -void Fl_Wayland_Image_Surface_Driver::untranslate() { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_untranslate(); -} - - -Fl_RGB_Image* Fl_Wayland_Image_Surface_Driver::image() { - if (shape_data_ && shape_data_->mask_pattern_) { - // draw above the secondary offscreen the main offscreen masked by mask_pattern_ - cairo_t *c = ((Fl_Cairo_Graphics_Driver*)driver())->cr(); - cairo_pattern_t *paint_pattern = cairo_pattern_create_for_surface(cairo_get_target(c)); - cairo_set_source(shape_data_->bg_cr, paint_pattern); - cairo_mask(shape_data_->bg_cr, shape_data_->mask_pattern_); - cairo_pattern_destroy(paint_pattern); - // copy secondary offscreen to the main offscreen - cairo_pattern_t *pat = cairo_pattern_create_for_surface(cairo_get_target(shape_data_->bg_cr)); - cairo_scale(c, shape_data_->scale, shape_data_->scale); - cairo_set_source(c, pat), - cairo_paint(c); - cairo_pattern_destroy(pat); - // delete secondary offscreen - cairo_surface_t *surf; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf); - unsigned char *bits = cairo_image_surface_get_data(surf); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] bits; - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - Fl_Wayland_Graphics_Driver::offscreen_buffer((Fl_Offscreen)shape_data_->bg_cr); - delete[] off_->buffer; - free(off_); - cairo_destroy(shape_data_->bg_cr); - free(shape_data_); - shape_data_ = NULL; - } - - // Convert depth-4 image in draw_buffer to a depth-3 image while exchanging R and B colors - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf = - Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); - int height = int(off_buf->data_size / off_buf->stride); - uchar *rgb = new uchar[off_buf->width * height * 3]; - uchar *p = rgb; - uchar *q; - for (int j = 0; j < height; j++) { - q = off_buf->buffer + j*off_buf->stride; - for (int i = 0; i < off_buf->width; i++) { // exchange R and B colors, transmit G - *p = *(q+2); - *(p+1) = *(q+1); - *(p+2) = *q; - p += 3; q += 4; - } - } - Fl_RGB_Image *image = new Fl_RGB_Image(rgb, off_buf->width, height, 3); - image->alloc_array = 1; - return image; -} - - -void Fl_Wayland_Image_Surface_Driver::mask(const Fl_RGB_Image *mask) { - bool using_copy = false; - shape_data_ = (struct shape_data_type*)calloc(1, sizeof(struct shape_data_type)); - int W, H; - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf = - Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); - W = off_buf->width; - H = (int)(off_buf->data_size / off_buf->stride); - if (W != mask->data_w() || H != mask->data_h()) { - Fl_RGB_Image *copy = (Fl_RGB_Image*)mask->copy(W, H); - mask = copy; - using_copy = true; - } - shape_data_->mask_pattern_ = Fl_Cairo_Graphics_Driver::calc_cairo_mask(mask); - //duplicate current offscreen content to new cairo_t* shape_data_->bg_cr - int width, height; - printable_rect(&width, &height); - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - (struct Fl_Wayland_Graphics_Driver::draw_buffer*)calloc(1, - sizeof(struct Fl_Wayland_Graphics_Driver::draw_buffer)); - Fl_Wayland_Graphics_Driver::cairo_init(off_, W, H, - cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, W), - CAIRO_FORMAT_RGB24); - cairo_set_user_data(off_->cairo_, &Fl_Wayland_Graphics_Driver::key, off_, NULL); - shape_data_->bg_cr = off_->cairo_; - memcpy(off_->buffer, off_buf->buffer, off_buf->data_size); - shape_data_->scale = double(width) / W; - if (using_copy) delete mask; -} diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H deleted file mode 100644 index 83efd79a3..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H +++ /dev/null @@ -1,193 +0,0 @@ -// -// Definition of the Wayland Screen interface -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2026 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 -// - -/** - \file Fl_Wayland_Screen_Driver.H - \brief Definition of Wayland Screen interface - */ - -#ifndef FL_WAYLAND_SCREEN_DRIVER_H -#define FL_WAYLAND_SCREEN_DRIVER_H - -#include <config.h> -#include "../Unix/Fl_Unix_Screen_Driver.H" -#include <wayland-client.h> - -class Fl_Window; - -class Fl_Wayland_Screen_Driver : public Fl_Unix_Screen_Driver -{ -private: - static int insertion_point_x; - static int insertion_point_y; - static int insertion_point_width; - static int insertion_point_height; - static bool insertion_point_location_is_valid; -public: -// type definitions - typedef enum {unspecified, MUTTER, WESTON, KWIN, OWL, WAYFIRE} compositor_name; - struct seat { - struct wl_seat *wl_seat; - struct wl_pointer *wl_pointer; - struct wl_keyboard *wl_keyboard; - uint32_t keyboard_enter_serial; - struct wl_surface *keyboard_surface; - struct wl_list pointer_outputs; - struct wl_cursor_theme *cursor_theme; - struct wl_cursor *default_cursor; - struct wl_surface *cursor_surface; - struct wl_surface *pointer_focus; - int pointer_scale; - uint32_t serial; - uint32_t pointer_enter_serial; - struct wl_data_device_manager *data_device_manager; - struct wl_data_device *data_device; - struct wl_data_source *data_source; - struct xkb_state *xkb_state; - struct xkb_context *xkb_context; - struct xkb_keymap *xkb_keymap; - struct xkb_compose_state *xkb_compose_state; - char *name; - struct zwp_text_input_v3 *text_input; - struct gtk_shell1 *gtk_shell; - }; - struct output { // one record for each screen - uint32_t id; - int x, y; // logical position of screen - int pixel_width; // in pixels - int pixel_height; // in pixels - int width; // in pixels, account for fractional scaling - int height; // in pixels, account for fractional scaling - float dpi; - struct wl_output *wl_output; - int wld_scale; // Wayland scale factor - float gui_scale; // FLTK scale factor - bool done; - struct wl_list link; - }; - enum cursor_shapes {arrow = 0, wait, insert, hand, help, cross, move, - north, south, west, east, north_south, west_east, south_west, south_east, north_east, north_west, nesw, nwse}; - static const int cursor_count = nwse + 1; // nber of elements of 'enum cursor_shapes' - -// static member variables - static FL_EXPORT struct wl_display *wl_display; - static const struct wl_data_device_listener *p_data_device_listener; - // next length of marked text after current marked text will have been replaced - static int next_marked_length; - static compositor_name compositor; // identifies the used Wayland compositor - -// static member functions - static void insertion_point_location(int x, int y, int height); - static bool insertion_point_location(int *px, int *py, int *pwidth, int *pheight); - static bool own_output(struct wl_output *output); - static void do_set_cursor(struct Fl_Wayland_Screen_Driver::seat *, - struct wl_cursor *wl_cursor = NULL, Fl_Cursor c = FL_CURSOR_NONE); -// member variables - struct wl_cursor *xc_cursor[cursor_count]; // one for each element of enum cursor_shapes - struct wl_registry *wl_registry; - struct wl_compositor *wl_compositor; - struct wl_subcompositor *wl_subcompositor; - struct wl_shm *wl_shm; - struct seat *seat; - struct wl_list outputs; // linked list of struct output records for all screens in system - struct libdecor *libdecor_context; - struct xdg_wm_base *xdg_wm_base; - struct zwp_text_input_manager_v3 *text_input_base; -#if HAVE_XDG_DIALOG - struct xdg_wm_dialog_v1 *xdg_wm_dialog; -#endif -#if HAVE_CURSOR_SHAPE - struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager; - struct wp_cursor_shape_device_v1 *wp_cursor_shape_device; -#endif - -// constructor - Fl_Wayland_Screen_Driver(); - -// overridden functions from parent class Fl_Screen_Driver - APP_SCALING_CAPABILITY rescalable() FL_OVERRIDE { return PER_SCREEN_APP_SCALING; } - float scale(int n) FL_OVERRIDE; - void scale(int n, float f) FL_OVERRIDE; - // --- screen configuration - void init() FL_OVERRIDE; - int x() FL_OVERRIDE; - int y() FL_OVERRIDE; - int w() FL_OVERRIDE; - int h() FL_OVERRIDE; - void screen_xywh(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - void screen_dpi(float &h, float &v, int n=0) FL_OVERRIDE; - void screen_work_area(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - // --- audible output - void beep(int type) FL_OVERRIDE; - // --- global events - void flush() FL_OVERRIDE; - void grab(Fl_Window* win) FL_OVERRIDE; - // --- global colors - void get_system_colors() FL_OVERRIDE; - // this one is in fl_wayland_clipboard_dnd.cxx - int dnd(int unused) FL_OVERRIDE; - int compose(int &del) FL_OVERRIDE; - void compose_reset() FL_OVERRIDE; - Fl_RGB_Image *read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win, - bool may_capture_subwins, bool *did_capture_subwins) FL_OVERRIDE; - int get_mouse(int &x, int &y) FL_OVERRIDE; - void open_display_platform() FL_OVERRIDE; - void close_display() FL_OVERRIDE; - void display(const char *d) FL_OVERRIDE; - // --- compute dimensions of an Fl_Offscreen - void offscreen_size(Fl_Offscreen o, int &width, int &height) FL_OVERRIDE; - int has_marked_text() const FL_OVERRIDE; - // --- clipboard operations - // this one is in fl_wayland_clipboard_dnd.cxx - void copy(const char *stuff, int len, int clipboard, const char *type) FL_OVERRIDE; - // this one is in fl_wayland_clipboard_dnd.cxx - void paste(Fl_Widget &receiver, int clipboard, const char *type) FL_OVERRIDE; - // this one is in fl_wayland_clipboard_dnd.cxx - int clipboard_contains(const char *type) FL_OVERRIDE; - void set_spot(int font, int height, int x, int y, int w, int h, Fl_Window *win) FL_OVERRIDE; - void reset_spot() FL_OVERRIDE; - void *control_maximize_button(void *data) FL_OVERRIDE; - int event_key(int k) FL_OVERRIDE; - int get_key(int k) FL_OVERRIDE; - void enable_im() FL_OVERRIDE; - void disable_im() FL_OVERRIDE; - bool screen_boundaries_known() FL_OVERRIDE { return false; } - float base_scale(int numscreen) FL_OVERRIDE; - - // overridden functions from parent class Fl_Unix_Screen_Driver - int poll_or_select_with_delay(double time_to_wait) FL_OVERRIDE; - int poll_or_select() FL_OVERRIDE; - -// Wayland-specific member functions - void screen_count_set(int count) {num_screens = count;} - int screen_count_get() {return num_screens;} - void reset_cursor(); - // this one is in fl_wayland_clipboard_dnd.cxx - void copy_image(const unsigned char* data, int W, int H); - void init_workarea(); - void set_cursor(); - struct wl_cursor *default_cursor(); - void default_cursor(struct wl_cursor *cursor); - struct wl_cursor *cache_cursor(const char *cursor_name); - uint32_t get_serial(); - struct wl_seat *get_wl_seat(); - char *get_seat_name(); - struct xkb_keymap *get_xkb_keymap(); -}; - - -#endif // FL_WAYLAND_SCREEN_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx deleted file mode 100644 index 9199f3a5f..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx +++ /dev/null @@ -1,2204 +0,0 @@ -// -// Implementation of Wayland Screen interface -// -// Copyright 1998-2026 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 "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include "../../Fl_Scalable_Graphics_Driver.H" -#include <wayland-cursor.h> -#include "../../../libdecor/build/fl_libdecor.h" -#include "xdg-shell-client-protocol.h" -#include "../Posix/Fl_Posix_System_Driver.H" -#include <FL/Fl.H> -#include <FL/Fl_Image_Surface.H> -#include <FL/platform.H> -#include <FL/fl_ask.H> -#include <FL/filename.H> -#include <vector> -#include "../../print_button.h" -#include <dlfcn.h> -#include <linux/input.h> -#include <stdlib.h> -#include <xkbcommon/xkbcommon.h> -#include <xkbcommon/xkbcommon-compose.h> -#include "text-input-client-protocol.h" -#include "gtk-shell-client-protocol.h" -#if HAVE_XDG_DIALOG -# include "xdg-dialog-client-protocol.h" -#endif -#if HAVE_CURSOR_SHAPE -# include "cursor-shape-client-protocol.h" -#endif -#include <assert.h> -#include <sys/mman.h> -#include <poll.h> -#include <errno.h> -#include <string.h> // for strerror() -#include <map> -extern "C" { - bool libdecor_get_cursor_settings(char **theme, int *size); - bool fl_is_surface_from_GTK_titlebar (struct wl_surface *surface, struct libdecor_frame *frame, - bool *using_GTK); -} - -// set this to 1 for keyboard debug output, 0 for no debug output -#define DEBUG_KEYBOARD 0 - -#define fl_max(a,b) ((a) > (b) ? (a) : (b)) -#define fl_min(a,b) ((a) < (b) ? (a) : (b)) - -struct pointer_output { - Fl_Wayland_Screen_Driver::output* output; - struct wl_list link; -}; - -/* Implementation note: - -- 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. - * KWin 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 ::KWIN. - -- 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. - -- 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 KWin 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. -*/ - - -static std::vector<int> key_vector; // used by Fl_Wayland_Screen_Driver::event_key() -static struct wl_surface *gtk_shell_surface = NULL; - -Fl_Wayland_Screen_Driver::compositor_name Fl_Wayland_Screen_Driver::compositor = - Fl_Wayland_Screen_Driver::unspecified; - - -extern "C" { - 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 - - -void Fl_Wayland_Screen_Driver::do_set_cursor( - struct Fl_Wayland_Screen_Driver::seat *seat, struct wl_cursor *wl_cursor, Fl_Cursor cursor) { - /* - wl_cursor: when non-NULL means a custom cursor; - when NULL: - - with "Cursor shape" protocol, cursor is meaningful if != FL_CURSOR_NONE; - - with old-school cursors, seat->default_cursor gives the desired cursor. - cursor: used with "Cursor shape" protocol for enumerated cursor shape, otherwise equal to FL_CURSOR_NONE - */ - struct wl_cursor_image *image; - struct wl_buffer *buffer; - const int scale = seat->pointer_scale; - -#if HAVE_CURSOR_SHAPE - static std::map<int, int> cursor_shape_map = { - {FL_CURSOR_DEFAULT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT }, - {FL_CURSOR_ARROW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT }, - {FL_CURSOR_CROSS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR }, - {FL_CURSOR_WAIT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT }, - {FL_CURSOR_INSERT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT }, - {FL_CURSOR_HAND, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB }, - {FL_CURSOR_HELP, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP }, - {FL_CURSOR_MOVE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE }, - {FL_CURSOR_N, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE }, - {FL_CURSOR_E, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE }, - {FL_CURSOR_W, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE }, - {FL_CURSOR_S, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE }, - {FL_CURSOR_NS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE }, - {FL_CURSOR_WE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE }, - {FL_CURSOR_SW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE }, - {FL_CURSOR_SE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE }, - {FL_CURSOR_NE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE }, - {FL_CURSOR_NW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE }, - {FL_CURSOR_NESW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE }, - {FL_CURSOR_NWSE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE } - }; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->wp_cursor_shape_device && !wl_cursor) { - if (cursor != FL_CURSOR_NONE) wp_cursor_shape_device_v1_set_shape( - scr_driver->wp_cursor_shape_device, seat->pointer_enter_serial, cursor_shape_map[cursor]); - return; - } -#endif - - if ((!seat->cursor_theme && !wl_cursor) || !seat->wl_pointer) - 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->pointer_enter_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; -static uint32_t wld_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 || - wld_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 = wld_event_time; -} - - -struct wl_display *Fl_Wayland_Screen_Driver::wl_display = NULL; - - -static Fl_Window *event_coords_from_surface(struct wl_surface *surface, - wl_fixed_t surface_x, wl_fixed_t surface_y) { - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - if (!win) return NULL; - int delta_x = 0, delta_y = 0; - while (win->parent()) { - delta_x += win->x(); - delta_y += win->y(); - win = win->window(); - } - float f = Fl::screen_scale(win->screen_num()); - Fl::e_x = wl_fixed_to_int(surface_x) / f + delta_x; - Fl::e_x_root = Fl::e_x + win->x(); - Fl::e_y = wl_fixed_to_int(surface_y) / f + delta_y; - int *poffset = Fl_Window_Driver::menu_offset_y(win); - if (poffset) Fl::e_y -= *poffset; - Fl::e_y_root = Fl::e_y + win->y(); - return win; -} - -static Fl_Window *need_leave = 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 Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; - Fl_Window *win = event_coords_from_surface(surface, surface_x, surface_y); - static bool using_GTK = seat->gtk_shell && - (gtk_shell1_get_version(seat->gtk_shell) >= GTK_SURFACE1_TITLEBAR_GESTURE_SINCE_VERSION); - if (!win && using_GTK) { - // check whether surface is the headerbar of a GTK-decorated window - Fl_X *xp = Fl_X::first; - while (xp && using_GTK) { // all mapped windows - struct wld_window *xid = (struct wld_window*)xp->xid; - if (xid->kind == Fl_Wayland_Window_Driver::DECORATED && - fl_is_surface_from_GTK_titlebar(surface, xid->frame, &using_GTK)) { - gtk_shell_surface = surface; - break; - } - xp = xp->next; - } - } - if (!win) return; - //fprintf(stderr, "pointer_enter window=%p\n", Fl_Wayland_Window_Driver::surface_to_window(surface)); - seat->pointer_focus = surface; - // use custom cursor if present - struct wl_cursor *cursor = - fl_wl_xid(win)->custom_cursor ? fl_wl_xid(win)->custom_cursor->wl_cursor : NULL; - seat->serial = serial; - seat->pointer_enter_serial = serial; - Fl_Wayland_Screen_Driver::do_set_cursor(seat, cursor, Fl_Wayland_Window_Driver::driver(win)->standard_cursor()); - set_event_xy(win); - need_leave = NULL; - win = Fl_Wayland_Window_Driver::surface_to_window(surface); - // Caution: with an Fl_Tooltip this call can hide the window being entered (#1317) - if (!win->parent()) Fl::handle(FL_ENTER, win); -} - - -static void pointer_leave(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, struct wl_surface *surface) { - struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; - if (seat->pointer_focus == surface) seat->pointer_focus = NULL; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - gtk_shell_surface = NULL; - if (win) { - //fprintf(stderr, "pointer_leave window=%p [%s]\n", win, (win->parent()?"sub":"top")); - set_event_xy(win); - need_leave = win->top_window(); // we leave a sub or toplevel window - wl_display_roundtrip(fl_wl_display()); // pointer_enter to other win, if applicable, will run - if (need_leave) { // we really left the sub-or-top win and did not enter another - extern Fl_Window *fl_xmousewin; - fl_xmousewin = 0; - Fl::handle(FL_LEAVE, need_leave); - } - } -} - - -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 Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - Fl_Window *win = event_coords_from_surface(seat->pointer_focus, surface_x, surface_y); - if (!win) return; - if (Fl::grab() && !Fl::grab()->menu_window() && Fl::grab() != win) { - // If there's an active, non-menu grab() and the pointer is in a window other than - // the grab(), make e_x_root too large to be in any window - Fl::e_x_root = 1000000; - } - else if (Fl_Window_Driver::menu_parent(NULL) && // any kind of menu is active now, and - !win->menu_window() && // we enter a non-menu window - win != Fl_Window_Driver::menu_parent(NULL) // that's not the window below the menu - ) { - Fl::e_x_root = 1000000; // make it too large to be in any window - } -//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); - wld_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 Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - if (gtk_shell_surface && state == WL_POINTER_BUTTON_STATE_PRESSED && - button == BTN_MIDDLE) { - struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(seat->gtk_shell,gtk_shell_surface); - gtk_surface1_titlebar_gesture(gtk_surface, serial, seat->wl_seat, - GTK_SURFACE1_GESTURE_MIDDLE_CLICK); - gtk_surface1_release(gtk_surface); // very necessary - return; - } - seat->serial = serial; - int event = 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(seat->pointer_focus); - if (!win) return; - win = win->top_window(); - wld_event_time = time; - int b = 0; - // Fl::e_state &= ~FL_BUTTONS; // DO NOT reset the mouse button state! - 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; } - else if (button == BTN_BACK) { Fl::e_state |= FL_BUTTON4; b = 4; } // ? - else if (button == BTN_SIDE) { Fl::e_state |= FL_BUTTON4; b = 4; } // OK: Debian 12 - else if (button == BTN_FORWARD) { Fl::e_state |= FL_BUTTON5; b = 5; } // ? - else if (button == BTN_EXTRA) { Fl::e_state |= FL_BUTTON5; b = 5; } // OK: Debian 12 - } else { // must be WL_POINTER_BUTTON_STATE_RELEASED - 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; } - else if (button == BTN_BACK) { Fl::e_state &= ~FL_BUTTON4; b = 4; } // ? - else if (button == BTN_SIDE) { Fl::e_state &= ~FL_BUTTON4; b = 4; } // OK: Debian 12 - else if (button == BTN_FORWARD) { Fl::e_state &= ~FL_BUTTON5; b = 5; } // ? - else if (button == BTN_EXTRA) { Fl::e_state &= ~FL_BUTTON5; b = 5; } // OK: Debian 12 - } - 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 Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(seat->pointer_focus); - if (!win) return; - wld_event_time = time; - int delta = wl_fixed_to_int(value); - if (abs(delta) >= 10) delta /= 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) { - if (Fl::event_shift()) { // shift key pressed: send vertical mousewheel event - Fl::e_dx = 0; - Fl::e_dy = delta; - } else { // shift key not pressed (normal behavior): send horizontal mousewheel event - Fl::e_dx = delta; - Fl::e_dy = 0; - } - Fl::handle(FL_MOUSEWHEEL, win->top_window()); - } - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - if (Fl::event_shift()) { // shift key pressed: send horizontal mousewheel event - Fl::e_dx = delta; - Fl::e_dy = 0; - } else {// shift key not pressed (normal behavior): send vertical mousewheel event - 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 Fl_Wayland_Screen_Driver::seat *seat); - - -static void try_update_cursor(struct Fl_Wayland_Screen_Driver::seat *seat) { - if (wl_list_empty(&seat->pointer_outputs)) return; - 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); - Fl_Wayland_Screen_Driver::do_set_cursor(seat); - } -} - - -static void output_scale(void *data, struct wl_output *wl_output, int32_t factor); - - -static void cursor_surface_enter(void *data, - struct wl_surface *wl_surface, struct wl_output *wl_output) { - // Runs when the seat's cursor_surface enters a display - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::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); - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output); - output_scale(output, wl_output, output->wld_scale); // rescale custom cursors - // maintain custom or standard window cursor - Fl_Window *win = Fl::first_window(); - if (win) { - Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win); - struct wld_window *xid = fl_wl_xid(win); - if (xid->custom_cursor) Fl_Wayland_Screen_Driver::do_set_cursor(seat, xid->custom_cursor->wl_cursor); - else if (driver->cursor_default()) driver->set_cursor(driver->cursor_default()); - else win->cursor(driver->standard_cursor()); - } -} - - -static void cursor_surface_leave(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::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); - } - } - try_update_cursor(seat); - // maintain custom window cursor - Fl_Window *win = Fl::first_window(); - if (win) { - struct wld_window *xid = fl_wl_xid(win); - if (xid->custom_cursor) Fl_Wayland_Screen_Driver::do_set_cursor(seat, xid->custom_cursor->wl_cursor); - } -} - - -static struct wl_surface_listener cursor_surface_listener = { - cursor_surface_enter, - cursor_surface_leave, -}; - - -static void init_cursors(struct Fl_Wayland_Screen_Driver::seat *seat) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - 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); - } -#if HAVE_CURSOR_SHAPE - if (scr_driver->wp_cursor_shape_manager) return; -#endif - - char *name; - int size; - struct wl_cursor_theme *theme; - - if (!libdecor_get_cursor_settings(&name, &size)) { - name = NULL; - size = 24; - } - size *= seat->pointer_scale; - theme = wl_cursor_theme_load(name, size, scr_driver->wl_shm); - free(name); - 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_cursor[Fl_Wayland_Screen_Driver::arrow] = - wl_cursor_theme_get_cursor(seat->cursor_theme, "left_ptr"); - } -} - - -static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, - uint32_t format, int32_t fd, uint32_t size) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); - - char *map_shm = (char*)mmap(NULL, size, PROT_READ, - wl_keyboard_get_version(wl_keyboard) >= 7 ? MAP_PRIVATE : 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); - if (xkb_keymap) { - struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); - xkb_keymap_unref(seat->xkb_keymap); - if (seat->xkb_state) xkb_state_unref(seat->xkb_state); - seat->xkb_keymap = xkb_keymap; - seat->xkb_state = xkb_state; - } -} - - -static int search_int_vector(std::vector<int>& v, int val) { - for (unsigned pos = 0; pos < v.size(); pos++) { - if (v[pos] == val) return pos; - } - return -1; -} - - -static void remove_int_vector(std::vector<int>& v, int val) { - int pos = search_int_vector(v, val); - if (pos < 0) return; - v.erase(v.begin()+pos); -} - - -static int process_wld_key(struct xkb_state *xkb_state, uint32_t key, - uint32_t *p_keycode, xkb_keysym_t *p_sym) { - uint32_t keycode = key + 8; - xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, keycode); - if (sym == 0xfe20) sym = FL_Tab; - if (sym == 0xffeb) sym = FL_Meta_L; // repair value libxkb gives for FL_Meta_L - if (sym == 0xffec) sym = FL_Meta_R; // repair value libxkb gives for FL_Meta_R - if (sym >= 'A' && sym <= 'Z') sym += 32; // replace uppercase by lowercase letter - int for_key_vector = sym; // for support of Fl::event_key(int) - // special processing for number keys == keycodes 10-19 : - if (keycode >= 10 && keycode <= 18) { - for_key_vector = '1' + (keycode - 10); - } else if (keycode == 19) { - for_key_vector = '0'; - } - if (p_keycode) *p_keycode = keycode; - if (p_sym) *p_sym = sym; - return for_key_vector; -} - - -static uint32_t last_keydown_serial = 0; // serial of last keydown event - - -static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; -//fprintf(stderr, "keyboard enter fl_win=%p; keys pressed are: ", Fl_Wayland_Window_Driver::surface_to_window(surface)); - key_vector.clear(); - // Replace wl_array_for_each(p, keys) rejected by C++ - for (uint32_t *p = (uint32_t *)(keys)->data; - (const char *) p < ((const char *) (keys)->data + (keys)->size); - (p)++) { - int for_key_vector = process_wld_key(seat->xkb_state, *p, NULL, NULL); -//fprintf(stderr, "%d ", for_key_vector); - if (search_int_vector(key_vector, for_key_vector) < 0) { - key_vector.push_back(for_key_vector); - } - } -//fprintf(stderr, "\n"); - seat->keyboard_surface = surface; - seat->keyboard_enter_serial = serial; - last_keydown_serial = 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - if (win) { - Fl::handle(FL_FOCUS, win); - fl_wl_find(fl_wl_xid(win)); - } -} - - -struct key_repeat_data_t { - uint32_t serial; - 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 (last_keydown_serial == key_repeat_data->serial) { - 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; - -static int previous_cursor_x = 0, previous_cursor_y = 0, previous_cursor_h = 0; -static uint32_t commit_serial = 0; -static char *current_pre_edit = NULL; -static char *pending_pre_edit = NULL; -static char *pending_commit = NULL; - - -static void send_commit(struct zwp_text_input_v3 *zwp_text_input_v3) { - zwp_text_input_v3_commit(zwp_text_input_v3); - commit_serial++; -} - - -// 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 /*&& !current_pre_edit*/ && - (x != previous_cursor_x || y != previous_cursor_y || height != previous_cursor_h)) { - previous_cursor_x = x; - previous_cursor_y = y; - previous_cursor_h = height; - 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); - send_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]; - // letter+modifier key - int condition = (Fl::e_state & (FL_ALT | FL_META | FL_CTRL)) && ascii < 128 ; - // pressing modifier key - // FL_Shift_L, FL_Shift_R, FL_Control_L, FL_Control_R, FL_Caps_Lock - // FL_Meta_L, FL_Meta_R, FL_Alt_L, FL_Alt_R - condition |= ((Fl::e_keysym >= FL_Shift_L && Fl::e_keysym <= FL_Alt_R) || - Fl::e_keysym == FL_Alt_Gr); - // FL_Home FL_Left FL_Up FL_Right FL_Down FL_Page_Up FL_Page_Down FL_End - // FL_Print FL_Insert FL_Menu FL_Help and more - condition |= (Fl::e_keysym >= FL_Home && Fl::e_keysym <= FL_Num_Lock); - condition |= (Fl::e_keysym >= FL_F && Fl::e_keysym <= FL_F_Last); - condition |= Fl::e_keysym == FL_Tab || Fl::e_keysym == FL_Scroll_Lock || Fl::e_keysym == FL_Pause; -//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() { - if (!Fl_Wayland_Screen_Driver::wl_registry) open_display(); - Fl::compose_state = 0; - next_marked_length = 0; - if (seat->xkb_compose_state) 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_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 Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - seat->serial = serial; - static char buf[128]; - uint32_t keycode; - xkb_keysym_t sym; - int for_key_vector = process_wld_key(seat->xkb_state, key, &keycode, &sym); -#if (DEBUG_KEYBOARD) - xkb_keysym_get_name(sym, buf, sizeof(buf)); - const char *action = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release"); - fprintf(stderr, "wl_keyboard_key: key %s: sym: %-12s(%d) code:%u fl_win=%p, ", - action, buf, sym, keycode, - Fl_Wayland_Window_Driver::surface_to_window(seat->keyboard_surface)); -#endif - xkb_state_key_get_utf8(seat->xkb_state, keycode, buf, sizeof(buf)); -#if (DEBUG_KEYBOARD) - fprintf(stderr, "utf8: '%s' e_length=%d [%d]\n", buf, (int)strlen(buf), *buf); -#endif - Fl::e_keysym = Fl::e_original_keysym = for_key_vector; - if (!(Fl::e_state & FL_NUM_LOCK) && sym >= XKB_KEY_KP_Home && sym <= XKB_KEY_KP_Delete) { - // compute e_keysym and e_original_keysym for keypad number keys and '.|,' when NumLock is off - static const int table[11] = {FL_Home /* 7 */, FL_Left /* 4 */, FL_Up /* 8 */, - FL_Right /* 6 */, FL_Down /* 2 */, FL_Page_Up /* 9 */, - FL_Page_Down /* 3 */, FL_End /* 1 */, 0xff0b /* 5 */, - FL_Insert /* 0 */, FL_Delete /* .|, */}; - static const int table_original[11] = {0xffb7 /* 7 */, 0xffb4 /* 4 */, 0xffb8 /* 8 */, - 0xffb6 /* 6 */, 0xffb2 /* 2 */, 0xffb9 /* 9 */, - 0xffb3 /* 3 */, 0xffb1 /* 1 */, 0xffb5 /* 5 */, - 0xffb0 /* 0 */, 0xffac /* .|, */}; - Fl::e_keysym = table[sym - XKB_KEY_KP_Home]; - Fl::e_original_keysym = table_original[sym - XKB_KEY_KP_Home]; - for_key_vector = Fl::e_original_keysym; - } -#if (DEBUG_KEYBOARD) - fprintf(stderr, "wl_keyboard_key: e_keysym=%x e_original_keysym=%x\n", Fl::e_keysym, Fl::e_original_keysym); -#endif - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - if (search_int_vector(key_vector, for_key_vector) < 0) { - key_vector.push_back(for_key_vector); - } - } else { - last_keydown_serial = 0; - remove_int_vector(key_vector, for_key_vector); - } - Fl::e_text = buf; - Fl::e_length = (int)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 (seat->xkb_compose_state && 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 = (int)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 - - wld_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_Window_Driver::surface_to_window(seat->keyboard_surface) ); - if (win) { - set_event_xy(win); - Fl::e_is_click = 0; - Fl::handle(event, win); - } - if (event == FL_KEYDOWN && status == XKB_COMPOSE_NOTHING && - !(sym >= FL_Shift_L && sym <= FL_Alt_R)) { - // Handling of key repeats : - // Use serial argument rather than time to detect repeated keys because - // serial value changes at each key up or down in all tested OS and compositors, - // whereas time value changes in Ubuntu24.04 KDE/Plasma 5.27.11 and Ubuntu22.04 KDE/Plasma 5.24.7 - // but not in Debian-testing KDE/Plasma 5.27.10. - // Unexplained difference in behaviors of KDE/Plasma compositor: - // Consider KDE settings -> input -> keyboard -> when a key is held: repeat/do nothing. - // This setting (repeat) has key-down wayland events repeated when key is held under Debian/KDE - // but not under Ubuntu/KDE ! - key_repeat_data_t *key_repeat_data = new key_repeat_data_t; - key_repeat_data->serial = serial; - key_repeat_data->window = win; - last_keydown_serial = serial; - 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 Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; -//fprintf(stderr, "keyboard leave fl_win=%p\n", Fl_Wayland_Window_Driver::surface_to_window(surface)); - seat->keyboard_surface = NULL; - last_keydown_serial = 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - if (!win && Fl::focus()) win = Fl::focus()->top_window(); - if (win) Fl::handle(FL_UNFOCUS, win); - key_vector.clear(); -} - - -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 Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - xkb_state_update_mask(seat->xkb_state, mods_depressed, mods_latched, mods_locked, - 0, 0, group); - Fl::e_state &= ~(FL_SHIFT+FL_CTRL+FL_ALT+FL_META+FL_CAPS_LOCK+FL_NUM_LOCK); - 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_LOGO, - XKB_STATE_MODS_DEPRESSED)) Fl::e_state |= FL_META; - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_CAPS, - XKB_STATE_MODS_LOCKED)) Fl::e_state |= FL_CAPS_LOCK; - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_NUM, - XKB_STATE_MODS_LOCKED)) Fl::e_state |= FL_NUM_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); - zwp_text_input_v3_set_content_type(zwp_text_input_v3, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL); - 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); - } - send_commit(zwp_text_input_v3); -} - - -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); - send_commit(zwp_text_input_v3); - free(pending_pre_edit); pending_pre_edit = NULL; - free(current_pre_edit); current_pre_edit = NULL; - free(pending_commit); pending_commit = NULL; -} - - -static void send_text_to_fltk(const char *text, bool is_marked, struct wl_surface *current_surface) { -//printf("send_text_to_fltk(%s, %d)\n",text,is_marked); - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(current_surface); - Fl::e_text = text ? (char*)text : (char*)""; - Fl::e_length = text ? (int)strlen(text) : 0; - Fl::e_keysym = 'a'; // fake a simple key - set_event_xy(win); - Fl::e_is_click = 0; - if (is_marked) { // goes to widget as marked text - Fl_Wayland_Screen_Driver::next_marked_length = Fl::e_length; - Fl::handle(FL_KEYDOWN, win); - } else if (text) { - Fl_Wayland_Screen_Driver::next_marked_length = 0; - Fl::handle(FL_KEYDOWN, win); - Fl::compose_state = 0; - } else { - Fl_Wayland_Screen_Driver::next_marked_length = 0; - Fl::handle(FL_KEYDOWN, win); - } -} - - -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); - free(pending_pre_edit); - pending_pre_edit = text ? strdup(text) : NULL; -} - - -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); - free(pending_commit); - pending_commit = (text ? strdup(text) : NULL); -} - - -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"); - struct wl_surface *current_surface = (struct wl_surface*)data; - const bool bad_event = (serial != commit_serial); - if ((pending_pre_edit == NULL && current_pre_edit == NULL) || - (pending_pre_edit && current_pre_edit && strcmp(pending_pre_edit, current_pre_edit) == 0)) { - free(pending_pre_edit); pending_pre_edit = NULL; - } else { - free(current_pre_edit); - current_pre_edit = pending_pre_edit; - pending_pre_edit = NULL; - if (current_pre_edit) { - send_text_to_fltk(current_pre_edit, !bad_event, current_surface); - } else { - send_text_to_fltk(NULL, false, current_surface); - } - } - if (pending_commit) { - send_text_to_fltk(pending_commit, false, current_surface); - free(pending_commit); pending_commit = NULL; - } -} - - -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, -}; - - -void Fl_Wayland_Screen_Driver::enable_im() { - if (text_input_base && !seat->text_input) { - seat->text_input = zwp_text_input_manager_v3_get_text_input(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); - } -} - - -void Fl_Wayland_Screen_Driver::disable_im() { - if (seat->text_input) { - zwp_text_input_v3_disable(seat->text_input); - zwp_text_input_v3_commit(seat->text_input); - zwp_text_input_v3_destroy(seat->text_input); - seat->text_input = NULL; - free(pending_pre_edit); pending_pre_edit = NULL; - free(current_pre_edit); current_pre_edit = NULL; - free(pending_commit); pending_commit = NULL; - } -} - - -static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) -{ - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::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; -#if HAVE_CURSOR_SHAPE - if (scr_driver->wp_cursor_shape_manager) { - scr_driver->wp_cursor_shape_device = - wp_cursor_shape_manager_v1_get_pointer(scr_driver->wp_cursor_shape_manager, seat->wl_pointer); - } -#endif // HAVE_CURSOR_SHAPE - 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 = seat->xkb_context && (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; - } - scr_driver->enable_im(); -} - - -static void seat_name(void *data, struct wl_seat *wl_seat, const char *name) { - struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::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->x = int(x); - output->y = int(y); - 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->pixel_width = int(width); - output->pixel_height = int(height); - output->width = output->pixel_width; // until further notice - output->height = output->pixel_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) -{ - // Runs at startup and when desktop scale factor is changed or screen added - Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data; -//fprintf(stderr, "output_done output=%p\n",output); - Fl_X *xp = Fl_X::first; - while (xp) { // all mapped windows - struct wld_window *win = (struct wld_window*)xp->xid; - Fl_Window *W = win->fl_win; - if (win->buffer || W->as_gl_window()) { - if (W->as_gl_window()) { - wl_surface_set_buffer_scale(win->wl_surface, output->wld_scale); - Fl_Window_Driver::driver(W)->is_a_rescale(true); - W->resize(W->x(), W->y(), W->w(), W->h()); - Fl_Window_Driver::driver(W)->is_a_rescale(false); - } else { - Fl_Wayland_Graphics_Driver::buffer_release(win); - } - W->redraw(); - Fl_Window_Driver::driver(W)->flush(); - } - xp = xp->next; - } - output->done = true; - - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->screen_count_get() > 0) { // true when output_done runs after initial screen dectection - scr_driver->screen_count_set( wl_list_length(&(scr_driver->outputs)) ); - scr_driver->init_workarea(); - } -} - - -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); - // rescale cursors of windows that map here and have a custom cursor - Fl_Window *win = Fl::first_window(); - while (win) { - struct wld_window *xid = fl_wl_xid(win); - struct Fl_Wayland_Window_Driver::surface_output *s_output; - // get 1st screen where window appears - s_output = wl_container_of(xid->outputs.next, s_output, link); - if (xid->custom_cursor && output == s_output->output) { - Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win); - driver->set_cursor_4args(xid->custom_cursor->rgb, - xid->custom_cursor->hotx, xid->custom_cursor->hoty, false); - }; - win = Fl::next_window(win); - } -} - - -static struct wl_output_listener output_listener = { - output_geometry, - output_mode, - output_done, - output_scale -}; - - -struct pair_bool { - bool found_gtk_shell; - bool found_wf_shell; -}; - - -// Notice: adding use of unstable protocol "XDG output" would allow FLTK to be notified -// in real time of changes to the relative location of multiple displays; -// with the present code, that information is received at startup only. -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 version=%u\n", interface, version); - 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); - - } 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 Fl_Wayland_Screen_Driver::seat*)calloc(1, - sizeof(struct Fl_Wayland_Screen_Driver::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); - if (scr_driver->seat->xkb_context) { - 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); - if (table) { - 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 Fl_Wayland_Screen_Driver::seat*)calloc(1, - sizeof(struct Fl_Wayland_Screen_Driver::seat)); - scr_driver->seat->data_device_manager = - (struct wl_data_device_manager*)wl_registry_bind(wl_registry, id, - &wl_data_device_manager_interface, - fl_min(version, 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 2 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; -#ifdef WL_OUTPUT_RELEASE_SINCE_VERSION - const int used_version = WL_OUTPUT_RELEASE_SINCE_VERSION; -#else - const int used_version = 2; -#endif - output->wl_output = (struct wl_output*)wl_registry_bind(wl_registry, - id, &wl_output_interface, fl_min(used_version, version)); - 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); - // Put new screen in list of screens, but make sure it's not in list already - // which may occur after having removed a screen. - bool found = false; - Fl_Wayland_Screen_Driver::output *elt; - wl_list_for_each(elt, &scr_driver->outputs, link) { - if (elt == output) found = true; - } - if (!found) { // add to end of the linked list of displays - struct wl_list *e = &scr_driver->outputs; - while (e->next != &scr_driver->outputs) e = e->next; // move e to end of linked list - wl_list_insert(e, &output->link); - } -//fprintf(stderr, "wl_output: id=%d wl_output=%p \n", id, output->wl_output); - - } 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 (strstr(interface, "wf_shell_manager")) { - ((pair_bool*)user_data)->found_wf_shell = true; - } else if (strcmp(interface, "gtk_shell1") == 0) { - ((pair_bool*)user_data)->found_gtk_shell = true; - //fprintf(stderr, "Running the Mutter compositor\n"); - scr_driver->seat->gtk_shell = (struct gtk_shell1*)wl_registry_bind(wl_registry, id, - >k_shell1_interface, version); - } 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::KWIN; - //fprintf(stderr, "Running the KWin compositor\n"); - } else if (strncmp(interface, "zowl_mach_ipc", 13) == 0) { - Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::OWL; - //fprintf(stderr, "Running the Owl compositor\n"); - if (wl_list_length(&scr_driver->outputs) == 0) { - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)calloc(1, sizeof *output); - output->id = 1; - output->wld_scale = 1; - output->gui_scale = 1.f; - output->width = 1440; output->height = 900; - output->pixel_width = 1440; output->pixel_height = 900; - output->done = true; - wl_list_insert(&(scr_driver->outputs), &output->link); - scr_driver->screen_count_set(1); - } - } 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); -#if HAVE_XDG_DIALOG - } else if (strcmp(interface, xdg_wm_dialog_v1_interface.name) == 0) { - scr_driver->xdg_wm_dialog = (struct xdg_wm_dialog_v1 *) - wl_registry_bind(wl_registry, id, &xdg_wm_dialog_v1_interface, 1); -#endif // HAVE_XDG_DIALOG -#if HAVE_CURSOR_SHAPE - } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { - scr_driver->wp_cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *) - wl_registry_bind(wl_registry, id, &wp_cursor_shape_manager_v1_interface, 1); -#endif // HAVE_CURSOR_SHAPE - } -} - - -static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { - Fl_Wayland_Screen_Driver::output *output; -//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(); - bool has_removed_screen = false; - wl_list_for_each(output, &(scr_driver->outputs), link) { // all screens - if (output->id == name) { // the screen being removed - wl_list_remove(&output->link); - wl_output_destroy(output->wl_output); - free(output); - has_removed_screen = true; - break; - } - } - if (has_removed_screen) { - scr_driver->screen_count_set( wl_list_length(&(scr_driver->outputs)) ); - scr_driver->init_workarea(); - } -} - - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - - -static void wayland_socket_callback(int fd, struct wl_display *display) -{ - if (wl_display_prepare_read(display) == -1) { - wl_display_dispatch_pending(display); - return; - } - wl_display_flush(display); - struct pollfd fds = (struct pollfd) { fd, POLLIN, 0 }; - if (poll(&fds, 1, 0) <= 0) { - wl_display_cancel_read(display); - return; - } - if (fds.revents & (POLLERR | POLLHUP)) { - wl_display_cancel_read(display); - goto fatal; - } - if (wl_display_read_events(display) == -1) - goto fatal; - if (wl_display_dispatch_pending(display) == -1) - goto fatal; - return; -fatal: - if (wl_display_get_error(display) == EPROTO) { - const struct wl_interface *interface; - int code = wl_display_get_protocol_error(display, &interface, NULL); - Fl::fatal("Fatal error %d in Wayland protocol: %s", - code, (interface ? interface->name : "unknown") ); - } else { - Fl::fatal("Fatal error while communicating with Wayland server: %s", - strerror(errno)); - } -} - - -Fl_Wayland_Screen_Driver::Fl_Wayland_Screen_Driver() : Fl_Unix_Screen_Driver() { - libdecor_context = NULL; - seat = NULL; - text_input_base = NULL; - reset_cursor(); - wl_registry = NULL; -#if HAVE_XDG_DIALOG - xdg_wm_dialog = NULL; -#endif -#if HAVE_CURSOR_SHAPE - wp_cursor_shape_manager = NULL; - wp_cursor_shape_device = NULL; -#endif -} - - -static void sync_done(void *data, struct wl_callback *cb, uint32_t time) { - // runs after all calls to registry_handle_global() - *(struct wl_callback **)data = NULL; - wl_callback_destroy(cb); - // keep processing until output_done() has run for each screen - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &scr_driver->outputs, link) { // each screen of the system - while (!output->done) wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - } - // Now all screens have been initialized - scr_driver->screen_count_set( wl_list_length(&(scr_driver->outputs)) ); - struct pair_bool *pair = (struct pair_bool*)wl_registry_get_user_data(scr_driver->wl_registry); - if (pair->found_gtk_shell || pair->found_wf_shell) { - Fl_Wayland_Screen_Driver::compositor = (pair->found_wf_shell ? - Fl_Wayland_Screen_Driver::WAYFIRE : Fl_Wayland_Screen_Driver::MUTTER); - } - if (scr_driver->seat) { -#if HAVE_CURSOR_SHAPE - if (!scr_driver->wp_cursor_shape_manager) -#endif - try_update_cursor(scr_driver->seat); - } - if (Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::OWL) scr_driver->init_workarea(); -} - - -static const struct wl_callback_listener sync_listener = { - sync_done -}; - - -static void do_atexit() { - if (Fl_Wayland_Screen_Driver::wl_display) { - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - } -} - - -void Fl_Wayland_Screen_Driver::open_display_platform() { - static bool beenHereDoneThat = false; - if (beenHereDoneThat) - return; - - beenHereDoneThat = true; - - if (!wl_display) { - wl_display = wl_display_connect(NULL); - if (!wl_display) { - Fl::fatal("No Wayland connection\n"); - } - } - //puts("Using Wayland backend"); - wl_list_init(&outputs); - - wl_registry = wl_display_get_registry(wl_display); - struct pair_bool pair = {false, false}; - wl_registry_add_listener(wl_registry, ®istry_listener, &pair); - struct wl_callback *registry_cb = wl_display_sync(wl_display); - wl_callback_add_listener(registry_cb, &sync_listener, ®istry_cb); - while (registry_cb) wl_display_dispatch(wl_display); - Fl::add_fd(wl_display_get_fd(wl_display), FL_READ, (Fl_FD_Handler)wayland_socket_callback, - wl_display); - fl_create_print_window(); - /* This is useful to avoid crash of the Wayland compositor after - FLTK apps terminate in certain situations: - - gnome-shell version < 44 (e.g. version 42.9) - - focus set to "follow-mouse" - See issue #821 for details. - */ - atexit(do_atexit); -} - - -void Fl_Wayland_Screen_Driver::close_display() { - if (!Fl_Wayland_Screen_Driver::wl_display) return; - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - if (text_input_base) { - disable_im(); - zwp_text_input_manager_v3_destroy(text_input_base); - text_input_base = NULL; - } - while (wl_list_length(&outputs) > 0) { - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - wl_list_remove(&output->link); - screen_count_set( wl_list_length(&outputs) ); - if (output->wl_output) { -#ifdef WL_OUTPUT_RELEASE_SINCE_VERSION - if (wl_output_get_version(output->wl_output) >= WL_OUTPUT_RELEASE_SINCE_VERSION) - wl_output_release(output->wl_output); - else -#endif - wl_output_destroy(output->wl_output); - } - free(output); - break; - } - } - wl_subcompositor_destroy(wl_subcompositor); wl_subcompositor = NULL; - wl_surface_destroy(seat->cursor_surface); seat->cursor_surface = NULL; - if (seat->cursor_theme) { - wl_cursor_theme_destroy(seat->cursor_theme); - seat->cursor_theme = NULL; - } - wl_compositor_destroy(wl_compositor); wl_compositor = NULL; - // wl_shm-related data - if (Fl_Wayland_Graphics_Driver::current_pool) { - struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data *pool_data = - (struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data*) - wl_shm_pool_get_user_data(Fl_Wayland_Graphics_Driver::current_pool); - wl_shm_pool_destroy(Fl_Wayland_Graphics_Driver::current_pool); - Fl_Wayland_Graphics_Driver::current_pool = NULL; - /*int err = */munmap(pool_data->pool_memory, pool_data->pool_size); - //printf("close_display munmap(%p)->%d\n", pool_data->pool_memory, err); - free(pool_data); - } - wl_shm_destroy(wl_shm); wl_shm = NULL; - if (seat->wl_keyboard) { - if (seat->xkb_state) { - xkb_state_unref(seat->xkb_state); - seat->xkb_state = NULL; - } - if (seat->xkb_keymap) { - xkb_keymap_unref(seat->xkb_keymap); - seat->xkb_keymap = NULL; - } - wl_keyboard_destroy(seat->wl_keyboard); - seat->wl_keyboard = NULL; - } - wl_pointer_destroy(seat->wl_pointer); seat->wl_pointer = NULL; - if (seat->xkb_compose_state) { - xkb_compose_state_unref(seat->xkb_compose_state); - seat->xkb_compose_state = NULL; - } - if (seat->xkb_context) { - xkb_context_unref(seat->xkb_context); - seat->xkb_context = NULL; - } - if (seat->data_source) { - wl_data_source_destroy(seat->data_source); - seat->data_source = NULL; - } - wl_data_device_destroy(seat->data_device); seat->data_device = NULL; - wl_data_device_manager_destroy(seat->data_device_manager); - seat->data_device_manager = NULL; - wl_seat_destroy(seat->wl_seat); seat->wl_seat = NULL; - if (seat->name) free(seat->name); - free(seat); seat = NULL; - if (libdecor_context) { - libdecor_unref(libdecor_context); - libdecor_context = NULL; - } - xdg_wm_base_destroy(xdg_wm_base); xdg_wm_base = NULL; - Fl_Wayland_Plugin *plugin = Fl_Wayland_Window_Driver::gl_plugin(); - if (plugin) plugin->terminate(); -#if HAVE_XDG_DIALOG - if (xdg_wm_dialog) { - xdg_wm_dialog_v1_destroy(xdg_wm_dialog); - xdg_wm_dialog = NULL; - } -#endif // HAVE_XDG_DIALOG -#if HAVE_CURSOR_SHAPE - if (wp_cursor_shape_device ) { - wp_cursor_shape_device_v1_destroy(wp_cursor_shape_device); - wp_cursor_shape_device = NULL; - } - if (wp_cursor_shape_manager ) { - wp_cursor_shape_manager_v1_destroy(wp_cursor_shape_manager); - wp_cursor_shape_manager = NULL; - } -#endif // HAVE_CURSOR_SHAPE - - Fl::remove_fd(wl_display_get_fd(Fl_Wayland_Screen_Driver::wl_display)); - wl_registry_destroy(wl_registry); wl_registry = NULL; - wl_display_disconnect(Fl_Wayland_Screen_Driver::wl_display); - Fl_Wayland_Screen_Driver::wl_display = NULL; - delete Fl_Display_Device::display_device()->driver(); - delete Fl_Display_Device::display_device(); - delete Fl::system_driver(); - delete this; -} - - -struct configure_s { int W, H; uint32_t state; }; - -static void xdg_toplevel_configure(void *v, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, struct wl_array *states) -{ - struct configure_s *data = (struct configure_s*)v; - data->W = width; - data->H = height; - data->state = (width && height && states ? *(uint32_t *)(states->data) : 0); -} - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure, -}; - - -static bool compute_full_and_maximized_areas(Fl_Wayland_Screen_Driver::output *output, - int& Wfullscreen, int& Hfullscreen, - int& Wworkarea, int& Hworkarea) { - if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::unspecified) { - Wfullscreen = 0; - return false; - } - bool found_workarea = false; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct wl_surface *wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor); - wl_surface_set_opaque_region(wl_surface, NULL); - struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, wl_surface); - struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); - struct configure_s data = {0, 0, 0}; - xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, &data); - xdg_toplevel_set_fullscreen(xdg_toplevel, output->wl_output); - wl_surface_commit(wl_surface); // necessary under KWin - while (data.state != XDG_TOPLEVEL_STATE_FULLSCREEN) - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - Wfullscreen = data.W; - Hfullscreen = data.H; - if (Wfullscreen && Hfullscreen && wl_list_length(&scr_driver->outputs) == 1) { - struct wl_surface *wl_surface2 = wl_compositor_create_surface(scr_driver->wl_compositor); - struct xdg_surface *xdg_surface2 = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, wl_surface2); - struct xdg_toplevel *xdg_toplevel2 = xdg_surface_get_toplevel(xdg_surface2); - struct configure_s data2 = {0, 0, 0}; - xdg_toplevel_add_listener(xdg_toplevel2, &xdg_toplevel_listener, &data2); - xdg_toplevel_set_parent(xdg_toplevel2, xdg_toplevel); - xdg_toplevel_set_maximized(xdg_toplevel2); - wl_surface_commit(wl_surface2); // necessary under KWin - while (data2.state != XDG_TOPLEVEL_STATE_MAXIMIZED) - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - Wworkarea = data2.W; - Hworkarea = data2.H; - xdg_toplevel_destroy(xdg_toplevel2); - xdg_surface_destroy(xdg_surface2); - wl_surface_destroy(wl_surface2); - if (Wworkarea == Wfullscreen && Hworkarea < Hfullscreen && Hworkarea > Hfullscreen - 80) - found_workarea = true; - if (Hworkarea == Hfullscreen && Wworkarea < Wfullscreen && Wworkarea > Wfullscreen - 80) - found_workarea = true; - } else { - Wworkarea = Wfullscreen; - Hworkarea = Hfullscreen; - } - xdg_toplevel_destroy(xdg_toplevel); - xdg_surface_destroy(xdg_surface); - wl_surface_destroy(wl_surface); - /*int fractional_scale = int(100 * (output->pixel_width / float(Wfullscreen))); - printf("fullscreen=%dx%d workarea=%dx%d fractional_scale=%d%% wld_s=%d\n", - Wfullscreen,Hfullscreen,Wworkarea,Hworkarea,fractional_scale,output->wld_scale);*/ - return found_workarea; -} - -static int workarea_xywh[4] = { -1, -1, -1, -1 }; - - -/* Implementation note about computing work area and about handling fractional scaling. - - FLTK computes 2 pairs of (WxH) values for each display: - 1) (pixel_width x pixel_height) gives the size in pixel of a display. It's unchanged by - any scaling applied by the compositor; it's assigned by function output_mode(). - 2) (width x height) gives the size in pixels of a buffer that would fully cover the display. - When the active scaling is non-fractional, these equations hold: - pixel_width = width = wld_scale * configured-width-of-fullscreen-window - pixel_height = height = wld_scale * configured-height-of-fullscreen-window - - When fractional scaling is active, buffers received from client are scaled down - by the compositor and mapped to screen. These equations hold: - pixel_width < width = wld_scale * configured-width-of-fullscreen-window - pixel_height < height = wld_scale * configured-height-of-fullscreen-window - - One way for a client to discover that fractional scaling is active on a given display - is to ask for a fullscreen window on that display, get its configured size and compare - it to that display's pixel size. That's what function compute_full_and_maximized_areas() does. - - One way for a client to discover the work area size of a display is to get the configured size - of a maximized window on that display. FLTK didn't find a way to control in general - on what display the compositor puts a maximized window. Therefore, FLTK computes an exact - work area size only when the system contains a single display. We create first a fullscreen - window on the display and then we create a maximized window made a child of the - fullscreen one and record its configured size. That's also done by function - compute_full_and_maximized_areas(). - */ - -void Fl_Wayland_Screen_Driver::init_workarea() -{ - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); // important after screen removal - bool need_init_workarea = true; - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - int Wfullscreen, Hfullscreen, Wworkarea, Hworkarea; - bool found_workarea = compute_full_and_maximized_areas(output, Wfullscreen, Hfullscreen, Wworkarea, Hworkarea); - if (Wfullscreen && Hfullscreen) { // skip sway which puts 0 there - output->width = Wfullscreen * output->wld_scale; // pixels - output->height = Hfullscreen * output->wld_scale; // pixels - if (found_workarea) { - workarea_xywh[0] = output->x; // pixels - workarea_xywh[1] = output->y; // pixels - workarea_xywh[2] = Wworkarea * output->wld_scale; // pixels - workarea_xywh[3] = Hworkarea * output->wld_scale; // pixels - need_init_workarea = false; - } - } - } - if (need_init_workarea) { - screen_xywh(workarea_xywh[0], workarea_xywh[1], workarea_xywh[2], workarea_xywh[3], 0); - } - Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL); -} - - -int Fl_Wayland_Screen_Driver::x() { - if (!Fl_Wayland_Screen_Driver::wl_registry) 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_Wayland_Screen_Driver::wl_registry) 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_Wayland_Screen_Driver::wl_registry) 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_Wayland_Screen_Driver::wl_registry) 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_Wayland_Screen_Driver::wl_registry) 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 / s; - Y = output->y / 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; - } - } - } -} - - -// Implements fl_beep(). See documentation in src/fl_ask.cxx. -void Fl_Wayland_Screen_Driver::beep(int type) -{ - fprintf(stderr, "\007"); -} - - -void Fl_Wayland_Screen_Driver::flush() -{ - if (Fl_Wayland_Screen_Driver::wl_display) { - wl_display_flush(Fl_Wayland_Screen_Driver::wl_display); - } -} - - -extern void fl_fix_focus(); // in Fl.cxx - - -void Fl_Wayland_Screen_Driver::grab(Fl_Window* win) -{ - 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 - 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); -} - - -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) { - struct wld_window* xid = win ? fl_wl_xid(win) : NULL; - if (win && (!xid || !xid->buffer)) return NULL; - struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer; - if (win) buffer = &xid->buffer->draw_buffer; - else { - Fl_Image_Surface_Driver *dr = (Fl_Image_Surface_Driver*)Fl_Surface_Device::surface(); - buffer = Fl_Wayland_Graphics_Driver::offscreen_buffer( - dr->image_surface()->offscreen()); - } - float s = win ? - Fl_Wayland_Window_Driver::driver(win)->wld_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->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) -{ - struct Fl_Wayland_Graphics_Driver::draw_buffer *off = Fl_Wayland_Graphics_Driver::offscreen_buffer(off_); - width = off->width; - height = off->data_size / off->stride; -} - - -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; - do_set_cursor(seat); -} - - -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() { - for (int i = 0; i < cursor_count; i++) xc_cursor[i] = 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; -} - - -int Fl_Wayland_Screen_Driver::get_mouse(int &xx, int &yy) { - open_display(); - xx = Fl::e_x_root; yy = Fl::e_y_root; - if (!seat->pointer_focus) return 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(seat->pointer_focus); - if (!win) return 0; - int snum = Fl_Window_Driver::driver(win)->screen_num(); -//printf("get_mouse(%dx%d)->%d\n", xx, yy, snum); - return snum; -} - - -void Fl_Wayland_Screen_Driver::set_spot(int font, int height, int x, int y, int w, int h, Fl_Window *win) { - Fl_Wayland_Screen_Driver::insertion_point_location(x, y, height); -} - - -void Fl_Wayland_Screen_Driver::reset_spot() { - Fl::compose_state = 0; - Fl_Wayland_Screen_Driver::next_marked_length = 0; - Fl_Wayland_Screen_Driver::insertion_point_location_is_valid = false; -} - - -void Fl_Wayland_Screen_Driver::display(const char *d) -{ - if (d && !wl_registry) { // if display was opened, it's too late - if (wl_display) { - // only the wl_display_connect() call was done, redo it because the target - // Wayland compositor may be different - wl_display_disconnect(wl_display); - } - wl_display = wl_display_connect(d); - if (!wl_display) { - fprintf(stderr, "Error: '%s' is not an active Wayland socket\n", d); - exit(1); - } - } -} - - -void *Fl_Wayland_Screen_Driver::control_maximize_button(void *data) { - // The code below aims at removing the calling window's fullscreen button - // while dialog runs. Unfortunately, it doesn't work with some X11 window managers - // (e.g., KDE, xfce) because the button goes away but doesn't come back, - // so we move this code to a virtual member function. - // Noticeably, this code works OK under Wayland. - struct win_dims { - Fl_Widget_Tracker *tracker; - int minw, minh, maxw, maxh; - struct win_dims *next; - }; - - if (!data) { // this call turns each decorated window's maximize button off - struct win_dims *first_dim = NULL; - // consider all bordered, top-level FLTK windows - Fl_Window *win = Fl::first_window(); - while (win) { - if (!win->parent() && win->border() && - !( ((struct wld_window*)Fl_X::flx(win)->xid)->state & - LIBDECOR_WINDOW_STATE_MAXIMIZED) ) { - win_dims *dim = new win_dims; - dim->tracker = new Fl_Widget_Tracker(win); - win->get_size_range(&dim->minw, &dim->minh, &dim->maxw, &dim->maxh, NULL, NULL, NULL); - //make win un-resizable - win->size_range(win->w(), win->h(), win->w(), win->h()); - dim->next = first_dim; - first_dim = dim; - } - win = Fl::next_window(win); - } - return first_dim; - } else { // this call returns each decorated window's maximize button to its previous state - win_dims *first_dim = (win_dims *)data; - while (first_dim) { - win_dims *dim = first_dim; - //give back win its resizing parameters - if (dim->tracker->exists()) { - Fl_Window *win = (Fl_Window*)dim->tracker->widget(); - win->size_range(dim->minw, dim->minh, dim->maxw, dim->maxh); - } - first_dim = dim->next; - delete dim->tracker; - delete dim; - } - return NULL; - } -} - - -int Fl_Wayland_Screen_Driver::poll_or_select_with_delay(double time_to_wait) { - if (wl_display_dispatch_pending(wl_display) > 0) return 1; - return Fl_Unix_Screen_Driver::poll_or_select_with_delay(time_to_wait); -} - - -// like Fl_Wayland_Screen_Driver::poll_or_select_with_delay(0.0) except no callbacks are done: -int Fl_Wayland_Screen_Driver::poll_or_select() { - int ret = wl_display_prepare_read(wl_display); - if (ret == 0) wl_display_cancel_read(wl_display); - else return 1; - return Fl_Unix_Screen_Driver::poll_or_select(); -} - - -int Fl_Wayland_Screen_Driver::event_key(int k) { - if (k >= 'A' && k <= 'Z') k += 32; - return (search_int_vector(key_vector, k) >= 0); -} - - -int Fl_Wayland_Screen_Driver::get_key(int k) { - return event_key(k); -} - - -float Fl_Wayland_Screen_Driver::base_scale(int numscreen) { - const char *p; - float factor = 1; - if ((p = fl_getenv("FLTK_SCALING_FACTOR"))) { - sscanf(p, "%f", &factor); - } - return factor; -} - - -struct wl_display *fl_wl_display() { - return Fl_Wayland_Screen_Driver::wl_display; -} diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H deleted file mode 100644 index c5c1bee50..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H +++ /dev/null @@ -1,185 +0,0 @@ -// -// Definition of Wayland window driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2025 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 -// - -/** - \file Fl_Wayland_Window_Driver.H - \brief Definition of Wayland window driver. - */ - -#ifndef FL_WAYLAND_WINDOW_DRIVER_H -#define FL_WAYLAND_WINDOW_DRIVER_H - -#include <config.h> -#include "../../Fl_Window_Driver.H" -#include <FL/Fl_Plugin.H> -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" - - -/* - Move everything here that manages the native window interface. - - There is one window driver for each Fl_Window. Window drivers manage window - actions such as resizing, events, decoration, fullscreen modes, etc. . All - drawing and rendering is managed by the Surface device and the associated - graphics driver. - - - window specific event handling - - window types and styles, depth, etc. - - decorations - */ - -typedef struct _cairo_pattern cairo_pattern_t; -typedef struct _cairo_rectangle_int cairo_rectangle_int_t; -class Fl_Wayland_Plugin; - - -class Fl_Wayland_Window_Driver : public Fl_Window_Driver -{ - friend class Fl_Wayland_Gl_Window_Driver; -private: - struct shape_data_type { - int lw_; ///< width of shape image - int lh_; ///< height of shape image - Fl_Image* shape_; ///< shape image - cairo_pattern_t *mask_pattern_; - } *shape_data_; - bool can_expand_outside_parent_; // specially to allow window docking (#987) - cairo_rectangle_int_t *subRect_; // makes sure subwindow remains inside its parent window - static bool in_flush_; // useful for progressive window drawing - Fl_Cursor standard_cursor_; // window's standard custom kind - struct gl_start_support *gl_start_support_; // for support of gl_start/gl_finish - bool is_popup_window_; -public: - inline Fl_Cursor standard_cursor() { return standard_cursor_; } - bool in_handle_configure; // distinguish OS and user window resize - - struct surface_output { // for linked list of displays where a surface maps - struct Fl_Wayland_Screen_Driver::output *output; - struct wl_list link; - }; - struct custom_cursor { - struct wl_cursor *wl_cursor; - const Fl_RGB_Image *rgb; - int hotx, hoty; - }; - static void delete_cursor(struct custom_cursor *custom, bool delete_rgb = true); - void decorated_win_size(int &w, int &h); - void shape_bitmap_(Fl_Image* b); - void shape_alpha_(Fl_Image* img, int offset) override; - FL_EXPORT int wld_scale(); // used by class Fl_Wayland_Gl_Window_Driver - cairo_rectangle_int_t *subRect() { return subRect_; } // getter - void subRect(cairo_rectangle_int_t *r); // setter - void checkSubwindowFrame(); - enum kind {DECORATED, SUBWINDOW, POPUP, UNFRAMED}; - struct xdg_toplevel *xdg_toplevel(); - Fl_Wayland_Window_Driver(Fl_Window*); - virtual ~Fl_Wayland_Window_Driver(); - static struct wld_window *wld_window; - static Fl_Window *surface_to_window(struct wl_surface *); - - static inline Fl_Wayland_Window_Driver* driver(const Fl_Window *w) { - return (Fl_Wayland_Window_Driver*)Fl_Window_Driver::driver(w); - } - static Fl_Wayland_Plugin *gl_plugin(); - - // --- window data - int decorated_w() override; - int decorated_h() override; - const Fl_Image* shape() override; - - // --- window management - void makeWindow() override; - void take_focus() override; - void flush() override; - void flush_overlay() override; - void draw_end() override; - void make_current() override; - void show() override; - void resize(int X,int Y,int W,int H) override; - void label(const char *name, const char *mininame) override; - void hide() override; - void map() override; - void unmap() override; - void fullscreen_on() override; - void fullscreen_off(int X, int Y, int W, int H) override; - void maximize() override; - void un_maximize() override; - void use_border() override; - void size_range() override; - void iconize() override; - void decoration_sizes(int *top, int *left, int *right, int *bottom) override; - // --- window cursor stuff - int set_cursor(Fl_Cursor) override; - int set_cursor(const Fl_RGB_Image*, int, int) override; - int set_cursor_4args(const Fl_RGB_Image*, int, int, bool); - - void shape(const Fl_Image* img) override; - void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, - Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) override; - int scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y, - void (*draw_area)(void*, int,int,int,int), void* data) override; - void wait_for_expose() override; - // menu-related stuff - void reposition_menu_window(int x, int y) override; - void menu_window_area(int &X, int &Y, int &W, int &H, int nscreen = -1) override; - static bool new_popup; // to support tall menu buttons - bool process_menu_or_tooltip(struct wld_window *); - static Fl_Window *previous_floatingtitle; // to support floating menuwindow w/ title - void allow_expand_outside_parent() override { can_expand_outside_parent_ = true; } -}; - - -struct wld_window { - Fl_Window *fl_win; - struct wl_list outputs; // linked list of displays where part or whole of window maps - struct wl_surface *wl_surface; - struct wl_callback *frame_cb; - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer; - struct xdg_surface *xdg_surface; - union { // for each value of kind - struct libdecor_frame *frame; - struct wl_subsurface *subsurface; - struct xdg_popup *xdg_popup; - struct xdg_toplevel *xdg_toplevel; - }; - // non-null when using custom cursor - struct Fl_Wayland_Window_Driver::custom_cursor *custom_cursor; -#if HAVE_XDG_DIALOG - struct xdg_dialog_v1 *xdg_dialog; -#endif - enum Fl_Wayland_Window_Driver::kind kind; - int configured_width; - int configured_height; - int floating_width; - int floating_height; - int state; - bool covered; // specially for Mutter and issue #878 -}; - - -class Fl_Wayland_Plugin : public Fl_Plugin { -public: - Fl_Wayland_Plugin(const char *pluginName) : Fl_Plugin(klass(), pluginName) { } - virtual const char *klass() { return "wayland.fltk.org"; } - virtual const char *name() = 0; - virtual void do_swap(Fl_Window*) = 0; - virtual void invalidate(Fl_Window*) = 0; - virtual void terminate() = 0; - virtual void destroy(struct gl_start_support *) = 0; -}; - -#endif // FL_WAYLAND_WINDOW_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx deleted file mode 100644 index 0495fb7bc..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx +++ /dev/null @@ -1,2191 +0,0 @@ -// -// Implementation of the Wayland window driver. -// -// Copyright 1998-2026 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 <FL/platform.H> -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include <FL/filename.H> -#include <wayland-cursor.h> -#include "../../../libdecor/build/fl_libdecor.h" -#include "xdg-shell-client-protocol.h" -#include "gtk-shell-client-protocol.h" -#if HAVE_XDG_DIALOG -# include "xdg-dialog-client-protocol.h" -#endif -#include <pango/pangocairo.h> -#include <FL/Fl_Overlay_Window.H> -#include <FL/Fl_Tooltip.H> -#include <FL/fl_draw.H> -#include <FL/fl_ask.H> -#include <FL/Fl.H> -#include <FL/Fl_Image_Surface.H> -#include <FL/Fl_Menu_Button.H> -#include <string.h> -#include <math.h> // for ceil() -#include <sys/types.h> // for pid_t -#include <unistd.h> // for getpid() - -struct cursor_image { // as in wayland-cursor.c of the Wayland project source code - struct wl_cursor_image image; - struct wl_cursor_theme *theme; - struct wl_buffer *buffer; - int offset; /* data offset of this image in the shm pool */ -}; - -extern "C" { -# include "../../../libdecor/src/libdecor-plugin.h" - uchar *fl_libdecor_titlebar_buffer(struct libdecor_frame *frame, int *w, int *h, int *stride); -} - -#define fl_max(a,b) ((a) > (b) ? (a) : (b)) -#define fl_min(a,b) ((a) < (b) ? (a) : (b)) - -#if !defined(FLTK_USE_X11) -Window fl_window = 0; -#endif - - -struct wld_window *Fl_Wayland_Window_Driver::wld_window = NULL; -bool Fl_Wayland_Window_Driver::new_popup = false; // to support tall menu buttons -// A menutitle to be mapped later as the child of a menuwindow -Fl_Window *Fl_Wayland_Window_Driver::previous_floatingtitle = NULL; - - -Fl_Wayland_Window_Driver::Fl_Wayland_Window_Driver(Fl_Window *win) : Fl_Window_Driver(win) -{ - shape_data_ = NULL; - standard_cursor_ = FL_CURSOR_DEFAULT; - in_handle_configure = false; - screen_num_ = -1; - gl_start_support_ = NULL; - subRect_ = NULL; - is_popup_window_ = false; - can_expand_outside_parent_ = false; -} - - -void Fl_Wayland_Window_Driver::delete_cursor( - struct Fl_Wayland_Window_Driver::custom_cursor *custom, bool delete_rgb) { - struct wl_cursor *wl_cursor = custom->wl_cursor; - struct cursor_image *new_image = (struct cursor_image*)wl_cursor->images[0]; - struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen = - (struct Fl_Wayland_Graphics_Driver::wld_buffer *) - wl_buffer_get_user_data(new_image->buffer); - struct wld_window fake_xid; - memset(&fake_xid, 0, sizeof(fake_xid)); - fake_xid.buffer = offscreen; - Fl_Wayland_Graphics_Driver::buffer_release(&fake_xid); - free(new_image); - free(wl_cursor->images); - free(wl_cursor->name); - free(wl_cursor); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->default_cursor() == wl_cursor) { - scr_driver->default_cursor(scr_driver->xc_cursor[Fl_Wayland_Screen_Driver::arrow]); - } - if (delete_rgb) delete custom->rgb; - delete custom; -} - - -Fl_Wayland_Window_Driver::~Fl_Wayland_Window_Driver() -{ - if (shape_data_) { - cairo_surface_t *surface; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surface); - uchar *data = cairo_image_surface_get_data(surface); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] data; - delete shape_data_; - } - if (subRect_) delete subRect_; - if (gl_start_support_) { // occurs only if gl_start/gl_finish was used - gl_plugin()->destroy(gl_start_support_); - } -} - - -void Fl_Wayland_Window_Driver::decorated_win_size(int &w, int &h) -{ - Fl_Window *win = pWindow; - w = win->w(); - h = win->h(); - if (!win->shown() || win->parent() || !win->border() || !win->visible()) return; - int X, titlebar_height; - libdecor_frame_translate_coordinate(fl_wl_xid(win)->frame, 0, 0, &X, &titlebar_height); -//printf("titlebar_height=%d\n",titlebar_height); - h = win->h() + ceil(titlebar_height / Fl::screen_scale(win->screen_num())); -} - - -int Fl_Wayland_Window_Driver::decorated_h() -{ - int w, h; - decorated_win_size(w, h); - return h; -} - - -int Fl_Wayland_Window_Driver::decorated_w() -{ - int w, h; - decorated_win_size(w, h); - return w; -} - - -struct xdg_toplevel *Fl_Wayland_Window_Driver::xdg_toplevel() { - struct wld_window * w = fl_wl_xid(pWindow); - struct xdg_toplevel *top = NULL; - if (w->kind == DECORATED) top = libdecor_frame_get_xdg_toplevel(w->frame); - else if (w->kind == UNFRAMED) top = w->xdg_toplevel; - return top; -} - - -void Fl_Wayland_Window_Driver::take_focus() -{ - struct wld_window *w = fl_wl_xid(pWindow); - if (w) { - Fl_Window *old_first = Fl::first_window(); - struct wld_window *first_xid = (old_first ? fl_wl_xid(old_first->top_window()) : NULL); - if (first_xid && first_xid != w && xdg_toplevel()) { - // this will move the target window to the front - Fl_Wayland_Window_Driver *top_dr = - Fl_Wayland_Window_Driver::driver(old_first->top_window()); - xdg_toplevel_set_parent(xdg_toplevel(), top_dr->xdg_toplevel()); - // this will remove the parent-child relationship - xdg_toplevel_set_parent(xdg_toplevel(), NULL); - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - } - // this sets the first window - fl_wl_find(w); - } -} - - -void Fl_Wayland_Window_Driver::flush_overlay() -{ - if (!shown()) return; - Fl_Overlay_Window *oWindow = pWindow->as_overlay_window(); - int erase_overlay = (pWindow->damage()&FL_DAMAGE_OVERLAY) | (overlay() == oWindow); - pWindow->clear_damage((uchar)(pWindow->damage()&~FL_DAMAGE_OVERLAY)); - pWindow->make_current(); - if (!other_xid) { - other_xid = new Fl_Image_Surface(oWindow->w(), oWindow->h(), 1); - oWindow->clear_damage(FL_DAMAGE_ALL); - } - if (oWindow->damage() & ~FL_DAMAGE_EXPOSE) { - Fl_X *myi = Fl_X::flx(pWindow); - fl_clip_region(myi->region); myi->region = 0; - Fl_Surface_Device::push_current(other_xid); - draw(); - Fl_Surface_Device::pop_current(); - } - if (erase_overlay) fl_clip_region(0); - if (other_xid) { - struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer = - Fl_Wayland_Graphics_Driver::offscreen_buffer(other_xid->offscreen()); - struct wld_window *xid = fl_wl_xid(pWindow); - struct Fl_Wayland_Graphics_Driver::wld_buffer *wbuffer = xid->buffer; - if (wbuffer->draw_buffer.data_size != buffer->data_size) { - fl_copy_offscreen(0, 0, oWindow->w(), oWindow->h(), other_xid->offscreen(), 0, 0); - } else { - memcpy(wbuffer->draw_buffer.buffer, buffer->buffer, wbuffer->draw_buffer.data_size); - } - } - if (overlay() == oWindow) oWindow->draw_overlay(); -} - - -const Fl_Image* Fl_Wayland_Window_Driver::shape() { - return shape_data_ ? shape_data_->shape_ : NULL; -} - - -void Fl_Wayland_Window_Driver::shape_bitmap_(Fl_Image* b) { - shape_data_->mask_pattern_ = Fl_Cairo_Graphics_Driver::bitmap_to_pattern( - (Fl_Bitmap*)b, true, NULL); - shape_data_->shape_ = b; - shape_data_->lw_ = b->data_w(); - shape_data_->lh_ = b->data_h(); -} - - -void Fl_Wayland_Window_Driver::shape_alpha_(Fl_Image* img, int offset) { - int i, j, d = img->d(), w = img->data_w(), h = img->data_h(); - int bytesperrow = cairo_format_stride_for_width(CAIRO_FORMAT_A1, w); - unsigned u; - uchar byte, onebit; - // build a CAIRO_FORMAT_A1 surface covering the non-fully transparent/black part of the image - uchar* bits = new uchar[h*bytesperrow]; // to store the surface data - const uchar* alpha = (const uchar*)*img->data() + offset; // points to alpha value of pixels - for (i = 0; i < h; i++) { - uchar *p = (uchar*)bits + i * bytesperrow; - byte = 0; - onebit = 1; - for (j = 0; j < w; j++) { - if (d == 3) { - u = *alpha; - u += *(alpha+1); - u += *(alpha+2); - } - else u = *alpha; - if (u > 0) { // if the pixel is not fully transparent/black - byte |= onebit; // turn on the corresponding bit of the bitmap - } - onebit = onebit << 1; // move the single set bit one position to the left - if (onebit == 0 || j == w-1) { - onebit = 1; - *p++ = ~byte; // store in bitmap one pack of bits, complemented - byte = 0; - } - alpha += d; // point to alpha value of next img pixel - } - } - cairo_surface_t *mask_surf = cairo_image_surface_create_for_data(bits, CAIRO_FORMAT_A1, - w, h, bytesperrow); - shape_data_->mask_pattern_ = cairo_pattern_create_for_surface(mask_surf); - cairo_surface_destroy(mask_surf); - shape_data_->shape_ = img; - shape_data_->lw_ = w; - shape_data_->lh_ = h; -} - - -void Fl_Wayland_Window_Driver::shape(const Fl_Image* img) { - if (shape_data_) { - if (shape_data_->mask_pattern_) { - cairo_surface_t *surface; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surface); - uchar *data = cairo_image_surface_get_data(surface); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] data; - } - } - else { - shape_data_ = new shape_data_type; - } - memset(shape_data_, 0, sizeof(shape_data_type)); - pWindow->border(false); - int d = img->d(); - if (d && img->count() >= 2) { - shape_pixmap_((Fl_Image*)img); - shape_data_->shape_ = (Fl_Image*)img; - } - else if (d == 0) shape_bitmap_((Fl_Image*)img); - else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1); - else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0); -} - - -void Fl_Wayland_Window_Driver::draw_end() -{ - if (shape_data_ && shape_data_->mask_pattern_) { - Fl_Wayland_Graphics_Driver *gr_dr = (Fl_Wayland_Graphics_Driver*)fl_graphics_driver; - cairo_t *cr = gr_dr->cr(); - cairo_matrix_t matrix; - cairo_matrix_init_scale(&matrix, double(shape_data_->lw_) / (pWindow->w() + 1), - double(shape_data_->lh_) / (pWindow->h() + 1) ); - cairo_matrix_translate(&matrix, 1, 1); - cairo_pattern_set_matrix(shape_data_->mask_pattern_, &matrix); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_mask(cr, shape_data_->mask_pattern_); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - } -} - - -/* Returns images of the captures of the window title-bar, and the left, bottom and right window borders - (or NULL if a particular border is absent). - Returned images can be deleted after use. Their depth and size may be platform-dependent. - The top and bottom images extend from left of the left border to right of the right border. - */ -void Fl_Wayland_Window_Driver::capture_titlebar_and_borders(Fl_RGB_Image*& top, - Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) -{ - top = left = bottom = right = NULL; - if (pWindow->decorated_h() == h()) return; - int htop = pWindow->decorated_h() - pWindow->h(); - struct wld_window *wwin = fl_wl_xid(pWindow); - int width, height, stride; - uchar *cairo_data = fl_libdecor_titlebar_buffer(wwin->frame, &width, &height, &stride); - if (!cairo_data) return; - uchar *data = new uchar[width * height * 4]; - uchar *p = data; - for (int j = 0; j < height; j++) { - uchar *q = cairo_data + j * stride; - for (int i = 0; i < width; i++) { - *p++ = *(q+2); // R - *p++ = *(q+1); // G - *p++ = *q; // B - *p++ = *(q+3); // A - q += 4; - } - } - top = new Fl_RGB_Image(data, width, height, 4); - top->alloc_array = 1; - top->scale(pWindow->w(), htop); -} - - -// make drawing go into this window (called by subclass flush() impl.) -void Fl_Wayland_Window_Driver::make_current() { - if (!shown()) { - static const char err_message[] = "Fl_Window::make_current(), but window is not shown()."; - fl_alert(err_message); - Fl::fatal(err_message); - } - - struct wld_window *window = fl_wl_xid(pWindow); - if (window->buffer) { - ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->needs_commit_tag( - &window->buffer->draw_buffer_needs_commit); - } - - // to support progressive drawing - if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->frame_cb) && - (!wait_for_expose_value) ) { - Fl_Wayland_Graphics_Driver::buffer_commit(window); - } - - Fl_Wayland_Window_Driver::wld_window = window; - fl_window = (Window)window; - float f = Fl::screen_scale(pWindow->screen_num()); - int wld_s = wld_scale(); - if (!window->buffer) { - window->buffer = Fl_Wayland_Graphics_Driver::create_wld_buffer( - int(pWindow->w() * f) * wld_s, int(pWindow->h() * f) * wld_s, false); - ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->needs_commit_tag( - &window->buffer->draw_buffer_needs_commit); - } - ((Fl_Wayland_Graphics_Driver*)fl_graphics_driver)->set_cairo( - window->buffer->draw_buffer.cairo_, f * wld_s); - ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->wld_scale = wld_s; - int *poffset = Fl_Window_Driver::menu_offset_y(pWindow); - if (poffset) { // for tall menu windows under KWIN to offset drawing inside window - cairo_translate(window->buffer->draw_buffer.cairo_, 0, *poffset); - } - cairo_rectangle_int_t *extents = subRect(); - if (extents) { // make damage-to-buffer not to leak outside parent - Fl_Region clip_region = fl_graphics_driver->XRectangleRegion(extents->x, extents->y, - extents->width, extents->height); -//printf("make_current: %dx%d %dx%d\n",extents->x, extents->y, extents->width, extents->height); - Fl_X::flx(pWindow)->region = clip_region; - } - else fl_graphics_driver->clip_region(0); - -#ifdef FLTK_HAVE_CAIROEXT - // update the cairo_t context - if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow); -#endif -} - - -void Fl_Wayland_Window_Driver::flush() { - if (!pWindow->damage()) return; - if (pWindow->as_gl_window()) { - int W = pWindow->w(); - int H = pWindow->h(); - float scale = fl_graphics_driver->scale(); - Fl_Wayland_Window_Driver::in_flush_ = true; - Fl_Window_Driver::flush(); - Fl_Wayland_Window_Driver::in_flush_ = false; - gl_plugin()->do_swap(pWindow); // useful only for GL win with overlay - if (scale != fl_graphics_driver->scale() || W != pWindow->w() || H != pWindow->h()) { - gl_plugin()->invalidate(pWindow); - } - return; - } - struct wld_window *window = fl_wl_xid(pWindow); - if (!window || !window->configured_width) return; - - Fl_X *ip = Fl_X::flx(pWindow); - cairo_region_t* r = (cairo_region_t*)ip->region; - if (!window->buffer || pWindow->as_overlay_window()) r = NULL; - - Fl_Wayland_Window_Driver::in_flush_ = true; - Fl_Window_Driver::flush(); - Fl_Wayland_Window_Driver::in_flush_ = false; - if (!window->frame_cb) Fl_Wayland_Graphics_Driver::buffer_commit(window, r); -} - - -void Fl_Wayland_Window_Driver::show() { - if (!shown()) { - fl_open_display(); - makeWindow(); - } else { - // Wayland itself gives no way to programmatically unminimize a minimized window - Fl::handle(FL_SHOW, pWindow); - } -} - - -static void popup_done(void *data, struct xdg_popup *xdg_popup); - - -static void destroy_surface_caution_pointer_focus(struct wl_surface *surface, - struct Fl_Wayland_Screen_Driver::seat *seat) { - if (seat->pointer_focus == surface) seat->pointer_focus = NULL; - if (seat->keyboard_surface == surface) seat->keyboard_surface = NULL; - wl_surface_destroy(surface); -} - - -void Fl_Wayland_Window_Driver::hide() { - if (pWindow == Fl_Screen_Driver::transient_scale_parent) { - // Delete also the running transient scale window - // because the transient is a popup and MUST be deleted - // before its parent. - Fl::remove_timeout(Fl_Screen_Driver::del_transient_window); - Fl_Screen_Driver::del_transient_window(NULL); - } - Fl_X* ip = Fl_X::flx(pWindow); - if (hide_common()) return; - if (ip->region) { - Fl_Graphics_Driver::default_driver().XDestroyRegion(ip->region); - ip->region = 0; - } - screen_num_ = -1; - struct wld_window *wld_win = (struct wld_window*)ip->xid; - if (wld_win) { // this test makes sure ip->xid has not been destroyed already - Fl_Wayland_Graphics_Driver::buffer_release(wld_win); - if (wld_win->kind == SUBWINDOW && wld_win->subsurface) { - wl_subsurface_destroy(wld_win->subsurface); - wld_win->subsurface = NULL; - } -#if HAVE_XDG_DIALOG - if (wld_win->xdg_dialog) { - xdg_dialog_v1_destroy(wld_win->xdg_dialog); - wld_win->xdg_dialog = NULL; - } -#endif - if (wld_win->kind == DECORATED) { - libdecor_frame_unref(wld_win->frame); - wld_win->frame = NULL; - wld_win->xdg_surface = NULL; - } else { - if (wld_win->kind == POPUP && wld_win->xdg_popup) { - popup_done(xdg_popup_get_user_data(wld_win->xdg_popup), wld_win->xdg_popup); - wld_win->xdg_popup = NULL; - } - if (wld_win->kind == UNFRAMED && wld_win->xdg_toplevel) { - xdg_toplevel_destroy(wld_win->xdg_toplevel); - wld_win->xdg_toplevel = NULL; - } - if (wld_win->xdg_surface) { - xdg_surface_destroy(wld_win->xdg_surface); - wld_win->xdg_surface = NULL; - } - } - if (wld_win->custom_cursor) delete_cursor(wld_win->custom_cursor); - if (wld_win->wl_surface) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - destroy_surface_caution_pointer_focus(wld_win->wl_surface, scr_driver->seat); - wld_win->wl_surface = NULL; - } - while (!wl_list_empty(&wld_win->outputs)) { // remove from screens where it belongs - struct surface_output *s_output; - s_output = wl_container_of(wld_win->outputs.next, s_output, link); - wl_list_remove(&s_output->link); - free(s_output); - } - if (Fl_Wayland_Window_Driver::wld_window == wld_win) { - Fl_Wayland_Window_Driver::wld_window = NULL; - } - if (wld_win->frame_cb) wl_callback_destroy(wld_win->frame_cb); // useful for GL subwins - free(wld_win); - } - delete ip; -} - - -void Fl_Wayland_Window_Driver::map() { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - if (wl_win->kind == SUBWINDOW && !wl_win->subsurface) { - struct wld_window *parent = fl_wl_xid(pWindow->window()); - if (parent) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - wl_win->subsurface = wl_subcompositor_get_subsurface(scr_driver->wl_subcompositor, - wl_win->wl_surface, parent->wl_surface); - float f = Fl::screen_scale(pWindow->top_window()->screen_num()); - wl_subsurface_set_position(wl_win->subsurface, pWindow->x() * f, pWindow->y() * f); - wl_subsurface_set_desync(wl_win->subsurface); // important - wl_subsurface_place_above(wl_win->subsurface, parent->wl_surface); - wl_win->configured_width = pWindow->w(); - wl_win->configured_height = pWindow->h(); - wait_for_expose_value = 0; - pWindow->redraw(); - } - } -} - - -void Fl_Wayland_Window_Driver::unmap() { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - if (wl_win->kind == SUBWINDOW && wl_win->wl_surface) { - wl_surface_attach(wl_win->wl_surface, NULL, 0, 0); - Fl_Wayland_Graphics_Driver::buffer_release(wl_win); - wl_subsurface_destroy(wl_win->subsurface); - wl_win->subsurface = NULL; - } -} - - -void Fl_Wayland_Window_Driver::size_range() { - if (shown()) { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - float f = Fl::screen_scale(pWindow->screen_num()); - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - if (wl_win->kind == DECORATED && wl_win->frame) { - int X,Y,W,H; - Fl::screen_work_area(X,Y,W,H, Fl::screen_num(x(),y(),w(),h())); - if (maxw && maxw < W && maxh && maxh < H) { - libdecor_frame_unset_capabilities(wl_win->frame, LIBDECOR_ACTION_FULLSCREEN); - } else { - libdecor_frame_set_capabilities(wl_win->frame, LIBDECOR_ACTION_FULLSCREEN); - } - if (maxw && maxh && (minw >= maxw || minh >= maxh)) { - libdecor_frame_unset_capabilities(wl_win->frame, LIBDECOR_ACTION_RESIZE); - } else { - libdecor_frame_set_capabilities(wl_win->frame, LIBDECOR_ACTION_RESIZE); - } - libdecor_frame_set_min_content_size(wl_win->frame, minw*f, minh*f); - libdecor_frame_set_max_content_size(wl_win->frame, maxw*f, maxh*f); - if (xdg_toplevel()) { - struct libdecor_state *state = libdecor_state_new(int(w() * f), int(h() * f)); - libdecor_frame_commit(wl_win->frame, state, NULL); - libdecor_state_free(state); - } - } else if (wl_win->kind == UNFRAMED && wl_win->xdg_toplevel) { - xdg_toplevel_set_min_size(wl_win->xdg_toplevel, minw*f, minh*f); - if (maxw && maxh) - xdg_toplevel_set_max_size(wl_win->xdg_toplevel, maxw*f, maxh*f); - } - } -} - - -void Fl_Wayland_Window_Driver::iconize() { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - if (wl_win->kind == DECORATED) { - libdecor_frame_set_minimized(wl_win->frame); - if (xdg_toplevel_get_version(xdg_toplevel()) < 6) { - Fl::handle(FL_HIDE, pWindow); - } - } - else if (wl_win->kind == UNFRAMED && wl_win->xdg_toplevel) xdg_toplevel_set_minimized(wl_win->xdg_toplevel); -} - - -void Fl_Wayland_Window_Driver::decoration_sizes(int *top, int *left, int *right, int *bottom) -{ - struct wld_window *xid = (struct wld_window*)fl_xid(pWindow); - if (xid && xid->kind == DECORATED) { - libdecor_frame_translate_coordinate(xid->frame, 0, 0, left, top); - *right = *left; - *bottom = 0; - } else { - Fl_Window_Driver::decoration_sizes(top, left, right, bottom); - } -} - - -int Fl_Wayland_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h, - int dest_x, int dest_y, - void (*draw_area)(void*, int,int,int,int), void* data) -{ - struct wld_window * xid = fl_wl_xid(pWindow); - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer = xid->buffer; - float s = wld_scale() * fl_graphics_driver->scale(); - if (s != 1) { - src_x = src_x * s; - src_y = src_y * s; - src_w = src_w * s; - src_h = src_h * s; - dest_x = dest_x * s; - dest_y = dest_y * s; - } - if (src_x == dest_x) { // vertical scroll - int i, to, step; - if (src_y > dest_y) { - i = 0; to = src_h; step = 1; - } else { - i = src_h - 1; to = -1; step = -1; - } - while (i != to) { - memcpy( - buffer->draw_buffer.buffer + (dest_y + i) * buffer->draw_buffer.stride + 4 * dest_x, - buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * src_x, - 4 * src_w); - i += step; - } - } else { // horizontal scroll - int i, to, step; - if (src_x > dest_x) { - i = 0; to = src_h; step = 1; - } else { - i = src_h - 1; to = -1; step = -1; - } - while (i != to) { - memmove( - buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * dest_x, - buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * src_x, - 4 * src_w); - i += step; - } - } - return 0; -} - - -static void handle_error(struct libdecor *libdecor_context, enum libdecor_error error, const char *message) -{ - Fl::fatal("Caught error (%d): %s\n", error, message); -} - - -static struct libdecor_interface libdecor_iface = { - .error = handle_error, -}; - - - -static void delayed_rescale(Fl_Window *win) { - Fl_Window_Driver::driver(win)->is_a_rescale(true); - win->size(win->w(), win->h()); - Fl_Window_Driver::driver(win)->is_a_rescale(false); -} - - -void change_scale(Fl_Wayland_Screen_Driver::output *output, struct wld_window *window, - float pre_scale) { - Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win); - if (!window->fl_win->parent()) { - // for top-level, set its screen number when the 1st screen for this surface changes - Fl_Wayland_Screen_Driver::output *running_output; - Fl_Wayland_Screen_Driver *scr_dr = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - int i = 0; - wl_list_for_each(running_output, &scr_dr->outputs, link) { // each screen of the system - if (running_output == output) { // we've found our screen of the system - win_driver->screen_num(i); - break; - } - i++; - } - } - float post_scale = Fl::screen_scale(win_driver->screen_num()) * output->wld_scale; -//printf("pre_scale=%.1f post_scale=%.1f\n", pre_scale, post_scale); - if (post_scale != pre_scale) { - if (window->kind == Fl_Wayland_Window_Driver::POPUP) { - Fl_Wayland_Graphics_Driver::buffer_release(window); - window->fl_win->redraw(); - } else { - // delaying the rescaling is necessary to set first the window's size_range according to the new screen - Fl::add_timeout(0, (Fl_Timeout_Handler)delayed_rescale, window->fl_win); - } - } -} - - -static void surface_enter(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) { - struct wld_window *window = (struct wld_window*)data; - - if (!Fl_Wayland_Screen_Driver::own_output(wl_output)) - return; - - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output); - if (output == NULL) - return; - - bool list_was_empty = wl_list_empty(&window->outputs); - struct Fl_Wayland_Window_Driver::surface_output *surface_output = - (struct Fl_Wayland_Window_Driver::surface_output*)malloc( - sizeof(struct Fl_Wayland_Window_Driver::surface_output)); - surface_output->output = output; - // add to end of the linked list of displays of this surface - struct wl_list *e = &window->outputs; - while (e->next != &window->outputs) e = e->next; // move e to end of linked list - wl_list_insert(e, &surface_output->link); -//printf("window %p enters screen id=%d length=%d\n", window->fl_win, output->id, wl_list_length(&window->outputs)); - if (list_was_empty && !window->fl_win->parent()) { - change_scale(output, window, 0); - } -} - - -static void surface_leave(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) { - if (!Fl_Wayland_Screen_Driver::own_output(wl_output)) - return; - struct wld_window *window = (struct wld_window*)data; - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output); - Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win); - float pre_scale = Fl::screen_scale(win_driver->screen_num()) * win_driver->wld_scale(); - struct Fl_Wayland_Window_Driver::surface_output *s_output; - int count = 0; - wl_list_for_each(s_output, &window->outputs, link) { - count++; - if (s_output->output == output) { - wl_list_remove(&s_output->link); - free(s_output); -//printf("window %p leaves screen id=%d length=%d\n", window->fl_win, output->id, wl_list_length(&window->outputs)); - break; - } - } - if (count == 1 && !wl_list_empty(&window->outputs) && !window->fl_win->parent()) { - s_output = wl_container_of(window->outputs.next, s_output, link); - change_scale(s_output->output, window, pre_scale); - } -} - - -static struct wl_surface_listener surface_listener = { - surface_enter, - surface_leave, -}; - - -Fl_Window *Fl_Wayland_Window_Driver::surface_to_window(struct wl_surface *surface) { - if (surface) { - if (wl_proxy_get_listener((struct wl_proxy *)surface) == &surface_listener) { - return ((struct wld_window *)wl_surface_get_user_data(surface))->fl_win; - } - } - return NULL; -} - - -static struct Fl_Wayland_Screen_Driver::output *screen_num_to_output(int num_screen) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - int i = 0; - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &(scr_driver->outputs), link) { // all screens of the system - if (i++ == num_screen) return output; - } - return NULL; -} - - -#define LIBDECOR_MR131 1 // this means libdecor does not include MR!131 yet - -#ifdef LIBDECOR_MR131 -/* === Beginning of hack that would become un-needed if libdecor accepted MR!131 === */ - -// true while the GUI is interactively resizing a decorated window -static bool in_decorated_window_resizing = false; - - -// libdecor's configure cb function for xdg_toplevel objects -static void (*decor_xdg_toplevel_configure)(void*, struct xdg_toplevel *, int32_t, - int32_t, struct wl_array *); - - -static void fltk_xdg_toplevel_configure(void *user_data, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, - struct wl_array *states) { - uint32_t *p; - in_decorated_window_resizing = false; - // Replace wl_array_for_each(p, states) rejected by C++ - for (p = (uint32_t *)(states)->data; - (const char *) p < ((const char *) (states)->data + (states)->size); - (p)++) { - if (*p == XDG_TOPLEVEL_STATE_RESIZING) { - in_decorated_window_resizing = true; - break; - } - } - decor_xdg_toplevel_configure(user_data, xdg_toplevel, width, height, states); -} - - -struct wl_object { // copied from wayland-private.h - const struct wl_interface *interface; - const void *implementation; - uint32_t id; -}; - - -// replace libdecor's toplevel configure cb by FLTK's -static void use_FLTK_toplevel_configure_cb(struct libdecor_frame *frame) { - struct wl_object *object = (struct wl_object *)libdecor_frame_get_xdg_toplevel(frame); - static struct xdg_toplevel_listener *fltk_listener = NULL; - if (!fltk_listener) { - struct xdg_toplevel_listener *decor_listener = (struct xdg_toplevel_listener*) - object->implementation; - fltk_listener = (struct xdg_toplevel_listener*) - malloc(sizeof(struct xdg_toplevel_listener)); - // initialize FLTK's listener with libdecor's values - *fltk_listener = *decor_listener; - // memorize libdecor's toplevel configure cb - decor_xdg_toplevel_configure = decor_listener->configure; - // replace libdecor's toplevel configure cb by FLTK's - fltk_listener->configure = fltk_xdg_toplevel_configure; - } - // replace the toplevel listener by a copy whose configure member is FLTK's - object->implementation = fltk_listener; -} - -/* === End of hack that would become un-needed if libdecor accepted MR!131 === */ -#endif // LIBDECOR_MR131 - - -// does win entirely cover its parent ? -static void does_window_cover_parent(Fl_Window *win) { - Fl_Window *parent = win->window(); - fl_wl_xid(parent)->covered = (win->x() <= 0 && win->y() <= 0 && - win->w() >= parent->w() && win->h() >= parent->h()); -} - - -// recursively explore all shown subwindows in a window and call f for each -static void scan_subwindows(Fl_Group *g, void (*f)(Fl_Window *)) { - for (int i = 0; i < g->children(); i++) { - Fl_Widget *o = g->child(i); - if (o->as_window()) { - if (!o->as_window()->shown()) continue; - f(o->as_window()); - } - if (o->as_group()) scan_subwindows(o->as_group(), f); - } -} - -// Generate FL_APP_ACTIVATE and FL_APP_DEACTIVATE events -static bool app_has_active_window = false; - -// If a window is deactivated, check after a short delay if any other window has -// become active. If not, send an FL_APP_DEACTIVATE event. -static void deferred_check_app_deactivate(void*) { - if (!app_has_active_window) return; - app_has_active_window = false; - // Check all FLTK windows to see if any are still active - for (Fl_Window *w = Fl::first_window(); w; w = Fl::next_window(w)) { - if (w->visible_r()) { - struct wld_window* xid = fl_wl_xid(w); - if (xid && (xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - app_has_active_window = true; - break; - } - } - } - if (!app_has_active_window) Fl::handle(FL_APP_DEACTIVATE, nullptr); -} - -static void handle_configure(struct libdecor_frame *frame, - struct libdecor_configuration *configuration, void *user_data) -{ - struct wld_window *window = (struct wld_window*)user_data; - if (!window->wl_surface) return; - int width, height; - enum libdecor_window_state window_state; - struct libdecor_state *state; - Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(window->fl_win); - // true exactly for the 1st run of handle_configure() for this window - bool is_1st_run = (window->xdg_surface == 0); - // true exactly for the 2nd run of handle_configure() for this window - bool is_2nd_run = (window->xdg_surface != 0 && driver->wait_for_expose_value); - float f = Fl::screen_scale(window->fl_win->screen_num()); - - if (!window->xdg_surface) window->xdg_surface = libdecor_frame_get_xdg_surface(frame); - -#ifdef LIBDECOR_MR131 - if (is_1st_run) use_FLTK_toplevel_configure_cb(frame); -#endif - struct wl_output *wl_output = NULL; - if (window->fl_win->fullscreen_active()) { - if (!(window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN)) { - if (Fl_Window_Driver::driver(window->fl_win)->force_position()) { - struct Fl_Wayland_Screen_Driver::output *output = - screen_num_to_output(window->fl_win->screen_num()); - if (output) wl_output = output->wl_output; - } - libdecor_frame_set_fullscreen(window->frame, wl_output); - } - } else if (driver->show_iconic()) { - libdecor_frame_set_minimized(window->frame); - driver->show_iconic(0); - } - if (!libdecor_configuration_get_window_state(configuration, &window_state)) - window_state = LIBDECOR_WINDOW_STATE_NONE; - if ((window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) && - !(window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) && !window->fl_win->border()) { - // necessary so Mutter correctly positions borderless window back from fullscreen - window->fl_win->redraw(); - } - window->state = window_state; - - // Weston, KWin, and some old versions of Mutter, on purpose, don't set the - // window width x height when xdg_toplevel_configure runs twice - // during resizable window creation - // (see https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/6). - // Consequently, libdecor_configuration_get_content_size() may return false twice. - // Weston and KWin, at least, don't change the window size asked by the client application - // which is available here in floating_{width,height}. - if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) { - if (is_2nd_run) { - width = window->floating_width; - height = window->floating_height; - if (!driver->is_resizable()) { - libdecor_frame_set_min_content_size(frame, width, height); - libdecor_frame_set_max_content_size(frame, width, height); - } - } else { width = height = 0; } - } - if (is_2nd_run && Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::MUTTER) { - scan_subwindows(window->fl_win, does_window_cover_parent); // issue #878 - } - - if (window->fl_win->fullscreen_active() && - Fl_Window_Driver::driver(window->fl_win)->force_position()) { - int X, Y, W, H; - Fl::screen_xywh(X, Y, W, H, window->fl_win->screen_num()); - width = W * f; height = H * f; - } - - if (width == 0) { - width = window->floating_width; - height = window->floating_height; - //fprintf(stderr,"handle_configure: using floating %dx%d\n",width,height); - } - -#ifndef LIBDECOR_MR131 - bool in_decorated_window_resizing = (window->state & LIBDECOR_WINDOW_STATE_RESIZING); -#endif - bool condition = in_decorated_window_resizing; - if (condition) { // see issue #878 - condition = (window->covered ? (window->buffer && window->buffer->in_use) : (window->frame_cb != NULL)); - } - if (condition) { - // Skip resizing & redrawing. The last resize request won't be skipped because - // LIBDECOR_WINDOW_STATE_RESIZING will be off or cb will be NULL then. - return; - } - - driver->in_handle_configure = true; - window->fl_win->resize(0, 0, ceil(width / f), ceil(height / f)); - driver->in_handle_configure = false; - if (wl_output) window->fl_win->redraw(); - window->configured_width = ceil(width / f); - window->configured_height = ceil(height / f); - if (is_2nd_run) driver->wait_for_expose_value = 0; -//fprintf(stderr, "handle_configure fl_win=%p size:%dx%d state=%x wait_for_expose_value=%d is_2nd_run=%d\n", window->fl_win, width,height,window_state,driver->wait_for_expose_value, is_2nd_run); - - // When no window is active, and one window gets activated, generate an FL_APP_ACTIVATE event - if (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) { - if (!app_has_active_window) { - app_has_active_window = true; - Fl::handle(FL_APP_ACTIVATE, nullptr); - } - - if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::WESTON) { - // After click on titlebar, weston calls wl_keyboard_enter() for a - // titlebar-related surface that FLTK can't identify, so we send FL_FOCUS here. - Fl::handle(FL_FOCUS, window->fl_win); - } - if (!window->fl_win->border()) libdecor_frame_set_visibility(window->frame, false); - else if (!libdecor_frame_is_visible(window->frame)) { - libdecor_frame_set_visibility(window->frame, true); - } else if (!window->fl_win->visible()) { - Fl::handle(FL_SHOW, window->fl_win); // useful when un-minimizing - } - } else if (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) { // window is minimized - Fl::handle(FL_HIDE, window->fl_win); - } - - // When a window gets deactivated and there are no other active windows, - // generate an FL_APP_DEACTIVATE event - if ( ((window_state & LIBDECOR_WINDOW_STATE_ACTIVE) == 0) && app_has_active_window) { - Fl::add_timeout(0.1, deferred_check_app_deactivate, nullptr); - } - - if (window->fl_win->border()) - driver->is_maximized(window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED); - if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) state = libdecor_state_new(width, - height); - else state = libdecor_state_new(int(ceil(width/f)*f), int(ceil(height/f)*f)); - libdecor_frame_commit(frame, state, configuration); - if (libdecor_frame_is_floating(frame)) { // store floating dimensions - window->floating_width = int(ceil(width/f)*f); - window->floating_height = int(ceil(height/f)*f); - //fprintf(stderr,"set floating_width+height %dx%d\n",width,height); - } - libdecor_state_free(state); - - driver->flush(); - if (Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::WESTON || !is_1st_run) { - window->fl_win->clear_damage(); - } -} - - -void Fl_Wayland_Window_Driver::wait_for_expose() -{ - Fl_Window_Driver::wait_for_expose(); - struct wld_window * xid = fl_wl_xid(pWindow); - if (!xid) return; - if (pWindow->fullscreen_active()) { - if (xid->kind == DECORATED) { - while (!(xid->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) || - !(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - } - } else if (xid->kind == UNFRAMED) { - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - } - } else if (xid->kind == DECORATED) { - // necessary for the windowfocus demo program with recent Wayland versions - if (!(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - } - } -} - - -static void delayed_close(Fl_Window *win) { - Fl::remove_check((Fl_Timeout_Handler)delayed_close, win); - Fl::handle(FL_CLOSE, win); -} - - -static void handle_close(struct libdecor_frame *frame, void *user_data) -{ // runs when the close button of a window titlebar is pushed - // or after "Quit" of the application menu - // or after the Kill command of Sway - Fl_Window* win = ((struct wld_window*)user_data)->fl_win; - int X, Y; - libdecor_frame_translate_coordinate(frame, 0, 0, &X, &Y); - if (Y == 0) Fl::handle(FL_CLOSE, win); - else { - // the close window attempt is delayed because libdecor - // uses the frame after return from this function - Fl::add_check((Fl_Timeout_Handler)delayed_close, win); - } -} - - -static void handle_commit(struct libdecor_frame *frame, void *user_data) -{ - struct wld_window* wl_win = (struct wld_window*)user_data; - if (wl_win->wl_surface) wl_surface_commit(wl_win->wl_surface); -} - - -static void handle_dismiss_popup(struct libdecor_frame *frame, const char *seat_name, void *user_data) -{ -} - - -static struct libdecor_frame_interface libdecor_frame_iface = { - handle_configure, - handle_close, - handle_commit, - handle_dismiss_popup, -}; - - -static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) -{ - // runs for borderless windows and popup (menu,tooltip) windows - struct wld_window *window = (struct wld_window*)data; - xdg_surface_ack_configure(xdg_surface, serial); -//fprintf(stderr, "xdg_surface_configure: surface=%p\n", window->wl_surface); - - if (window->fl_win->w() != window->configured_width || - window->fl_win->h() != window->configured_height) { - if (window->buffer) { - Fl_Wayland_Graphics_Driver::buffer_release(window); - } - } - window->configured_width = window->fl_win->w(); - window->configured_height = window->fl_win->h(); - Fl_Window_Driver::driver(window->fl_win)->flush(); - window->fl_win->clear_damage(); -} - - -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_configure, -}; - - -static bool parse_states_fullscreen(struct wl_array *states) -{ - uint32_t *p; - // Replace wl_array_for_each(p, states) rejected by C++ - for (p = (uint32_t *)(states)->data; - (const char *) p < ((const char *) (states)->data + (states)->size); - (p)++) { - if (*p == XDG_TOPLEVEL_STATE_FULLSCREEN) return true; - } - return false; -} - - -static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, struct wl_array *states) -{ - // runs for borderless top-level windows - // under Weston: width & height are 0 during both calls, except if fullscreen - struct wld_window *window = (struct wld_window*)data; -//fprintf(stderr, "xdg_toplevel_configure: surface=%p size: %dx%d\n", window->wl_surface, width, height); - if (window->fl_win->fullscreen_active() && !parse_states_fullscreen(states)) { - struct wl_output *wl_output = NULL; - if (Fl_Window_Driver::driver(window->fl_win)->force_position()) { - struct Fl_Wayland_Screen_Driver::output *output = - screen_num_to_output(window->fl_win->screen_num()); - if (output) wl_output = output->wl_output; - } - xdg_toplevel_set_fullscreen(xdg_toplevel, wl_output); - if (wl_output) { - int X, Y; - Fl::screen_xywh(X, Y, width, height, window->fl_win->screen_num()); - } - } - if (window->configured_width) { - Fl_Window_Driver::driver(window->fl_win)->wait_for_expose_value = 0; - } - float f = Fl::screen_scale(window->fl_win->screen_num()); - if (width == 0 || height == 0) { - width = window->fl_win->w() * f; - height = window->fl_win->h() * f; - } - window->fl_win->size(ceil(width / f), ceil(height / f)); - if (window->buffer && (ceil(width / f) != window->configured_width || - ceil(height / f) != window->configured_height)) { - Fl_Wayland_Graphics_Driver::buffer_release(window); - } - window->configured_width = ceil(width / f); - window->configured_height = ceil(height / f); -} - - -static void xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel) -{ -} - - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure, - .close = xdg_toplevel_close, -}; - - -struct win_positioner { - struct wld_window *window; - int x, y; - Fl_Window *child_popup; -}; - - -static void popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, - int32_t width, int32_t height) { - struct win_positioner *win_pos = (struct win_positioner *)data; - struct wld_window *window = win_pos->window; -//printf("popup_configure %p asked:%dx%d got:%dx%d\n",window->fl_win, win_pos->x,win_pos->y, x,y); - Fl_Window_Driver::driver(window->fl_win)->wait_for_expose_value = 0; - int HH; - Fl_Window_Driver::menu_parent(&HH); - if (window->fl_win->h() > HH && y != win_pos->y) { // A menu taller than the display - // Under KWin, height is set to the display height or less: we ignore that. - window->state = (y - win_pos->y); - // make selected item visible, if there's one - Fl_Window_Driver::scroll_to_selected_item(window->fl_win); - } - if (Fl_Window_Driver::current_menu_button && !Fl_Window_Driver::menu_leftorigin(window->fl_win)) { - int X, Y; - Fl_Window_Driver::current_menu_button->top_window_offset(X, Y); - if (y < Y) { - Fl_Window *win = window->fl_win; - win->Fl_Widget::resize(win->x(), Y - win->h(), win->w(), win->h()); - } - } -} - - -static struct xdg_popup *mem_grabbing_popup = NULL; - - -static void popup_done(void *data, struct xdg_popup *xdg_popup) { - struct win_positioner *win_pos = (struct win_positioner *)data; - struct wld_window *window = win_pos->window; -//fprintf(stderr, "popup_done: popup=%p data=%p xid=%p fl_win=%p\n", xdg_popup, data, window, window->fl_win); - if (win_pos->child_popup) win_pos->child_popup->hide(); - xdg_popup_destroy(xdg_popup); - delete win_pos; - // The sway compositor calls popup_done directly and hides the menu - // when the app looses focus. - // Thus, we hide the window so FLTK and Wayland are in matching states. - window->xdg_popup = NULL; - window->fl_win->hide(); - if (mem_grabbing_popup == xdg_popup) { - mem_grabbing_popup = NULL; - } -} - - -static const struct xdg_popup_listener popup_listener = { - .configure = popup_configure, - .popup_done = popup_done, -}; - - -bool Fl_Wayland_Window_Driver::in_flush_ = false; - - -static const char *get_prog_name() { - pid_t pid = getpid(); - char fname[100]; - snprintf(fname, 100, "/proc/%u/cmdline", pid); - FILE *in = fopen(fname, "r"); - if (in) { - static char line[200]; - const char *p = fgets(line, sizeof(line), in); - fclose(in); - p = strrchr(line, '/'); if (!p) p = line; else p++; - return p; - } - return "unknown"; -} - - -/* Implementation note about menu windows under Wayland. - Wayland offers a way to position popup windows such as menu windows using constraints. - Each popup is located relatively to a parent window which can be a popup itself and - MUST overlap or at least touch this parent. - Constraints determine how a popup is positioned relatively to a defined area (called - the anchor rectangle) of its parent popup/window and what happens when this position - would place the popup all or partly outside the display. - In contrast, FLTK computes the adequate positions of menu windows in the display using - knowledge about the display size and the location of the window in the display, and then - maps them at these positions. - These 2 logics are quite different because Wayland hides the position of windows inside the - display, whereas FLTK uses the location of windows inside the display to position popups. - Let's call "source window" the non-popup window above which all popups are mapped. - The approach implemented here is two-fold. - 1) If a menu window is not taller than the display, use Wayland constraints to position it. - Wayland imposes that the first constructed popup must overlap or touch the source window. - Other popups can be placed below, above, at right, or at left of a previous popup which - allows them to expand outside the source window, while constraints can ensure they won't - extend outside the display. - 2) A menu window taller than the display is initially mapped with the constraint to - begin at the top border of the display. This allows FLTK to know the distance between - the source window and the display top. FLTK can later reposition the same tall popup, - without the constraint not to go beyond the display top, at the exact position so that - the desired series of menu items appear in the visible part of the tall popup. - - In case 1) above, the values that represent the display bounds are given very - large values. That's done by member function Fl_Wayland_Window_Driver::menu_window_area(). - Consequently, FLTK computes an initial layout of future popups relatively to - the source window as if it was mapped on an infinitely large display. Then, the location - of the first popup to be mapped is modified if necessary so it overlaps or touches the - source window. Finally, other popups are located using Wayland logic below, above or to the - right of previous popups. Wayland constraints mechanism also allows a popup tentatively - placed below a previous one to be flipped above it if that prevents the popup from expanding - beyond display limits. This is used to unfold menu bar menus below or above the menu bar. - After each popup is created and scheduled for being mapped on display by function - process_menu_or_tooltip(), makeWindow() calls Fl_Window::wait_for_expose() so its constrained - position is known before computing the position of the next popup. This ensures each - popup is correctly placed relatively to its parent. - - Groups of popups containing a menutitle, the associated menuwindow, and optionally - a submenu window and that don't belong to an Fl_Menu_Bar are mapped in a different order: - the menuwindow is mapped first, and the menutitle is mapped second above it as a child popup. - Fl_Window_Driver::is_floating_title() detects when such a menutitle is created, - static member variable previous_floatingtitle is assigned the value of this menutitle, and - the menutitle is mapped only after the menuwindow has been mapped, as a child of it. - This positions better the popup group in the display relatively to where the popup - was created. - - In case 2) above, a tall popup is mapped with XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y - which puts its top at the display top border. The Wayland system then calls the - popup_configure() callback function with the x,y coordinates of the top left corner - where the popup is mapped relatively to an anchor point in the source window. - The difference between the asked window position and the effective position is stored - in the state member variable of the tall popup's struct wld_window. This information - allows FLTK to compute the distance between the source window top and the display top border. - Function Fl_Wayland_Window_Driver::menu_window_area() sets the top of the display to - a value such that function Fl_Wayland_Window_Driver::reposition_menu_window(), called by - menuwindow::autoscroll(int n), ensures that menu item #n is visible. Static boolean member - variable Fl_Wayland_Window_Driver::new_popup is useful to position tall menuwindows created - by an Fl_Menu_Button or Fl_Choice. It is set to true when any menu popup is created. - It is used each time menu_window_area() runs for a particular Fl_Menu_Button or Fl_Choice, - and is reset to false after its first use. This allows menu_window_area() to give the top of - the display an adequate value the first time and to keep this value next times it runs. - Fl_Window_Driver::scroll_to_selected_item() scrolls the tall popup so its selected - item, when there's one, is visible immediately after the tall popup is mapped on display. - */ - - -bool Fl_Wayland_Window_Driver::process_menu_or_tooltip(struct wld_window *new_window) { - // a menu window or tooltip - new_window->kind = Fl_Wayland_Window_Driver::POPUP; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (Fl_Window_Driver::is_floating_title(pWindow)) { - previous_floatingtitle = pWindow; - return true; - } - new_window->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, - new_window->wl_surface); - xdg_surface_add_listener(new_window->xdg_surface, &xdg_surface_listener, new_window); - Fl_Wayland_Window_Driver::new_popup = true; - Fl_Window *menu_origin = NULL; - if (pWindow->menu_window()) { - menu_origin = Fl_Window_Driver::menu_leftorigin(pWindow); - if (!menu_origin && !previous_floatingtitle) menu_origin = - Fl_Window_Driver::menu_title(pWindow); - } - Fl_Widget *target = (pWindow->tooltip_window() ? Fl_Tooltip::current() : NULL); - if (pWindow->user_data() == &Fl_Screen_Driver::transient_scale_display && - Fl_Screen_Driver::transient_scale_parent) { - target = Fl_Screen_Driver::transient_scale_parent; - } - if (!target) target = Fl_Window_Driver::menu_parent(); - if (!target) target = Fl::belowmouse(); - if (!target) target = Fl::first_window(); - Fl_Window *parent_win = target->top_window(); - while (parent_win && parent_win->menu_window() && driver(parent_win)->popup_window()) { - parent_win = Fl::next_window(parent_win); - } - Fl_Window *origin_win = (menu_origin ? menu_origin : parent_win); - struct wld_window * parent_xid = fl_wl_xid(origin_win); - struct xdg_surface *parent_xdg = parent_xid->xdg_surface; - float f = Fl::screen_scale(parent_win->screen_num()); - //fprintf(stderr, "menu parent_win=%p pos:%dx%d size:%dx%d\n", parent_win, pWindow->x(), pWindow->y(), pWindow->w(), pWindow->h()); -//printf("window=%p menutitle=%p bartitle=%d leftorigin=%p y=%d\n", pWindow, Fl_Window_Driver::menu_title(pWindow), Fl_Window_Driver::menu_bartitle(pWindow), Fl_Window_Driver::menu_leftorigin(pWindow), pWindow->y()); - struct xdg_positioner *positioner = xdg_wm_base_create_positioner(scr_driver->xdg_wm_base); - //xdg_positioner_get_version(positioner) <== gives 1 under Debian and Sway - int popup_x, popup_y; - if (Fl_Window_Driver::current_menu_button && !Fl_Window_Driver::menu_leftorigin(pWindow)) { - int X, Y; - Fl_Window_Driver::current_menu_button->top_window_offset(X, Y); - xdg_positioner_set_anchor_rect(positioner, X * f, Y * f, - Fl_Window_Driver::current_menu_button->w() * f, - Fl_Window_Driver::current_menu_button->h() * f); - popup_x = X * f; - popup_y = 0; - if (parent_xid->kind == Fl_Wayland_Window_Driver::DECORATED && !origin_win->fullscreen_active()) - libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, - &popup_x, &popup_y); - } else if (Fl_Window_Driver::menu_title(pWindow) && Fl_Window_Driver::menu_bartitle(pWindow)) { - xdg_positioner_set_anchor_rect(positioner, 0, 0, - Fl_Window_Driver::menu_title(pWindow)->w() * f, - Fl_Window_Driver::menu_title(pWindow)->h() * f); - popup_x = 0; - popup_y = Fl_Window_Driver::menu_title(pWindow)->h() * f; - } else { - popup_x = pWindow->x() * f; popup_y = pWindow->y() * f; - if (popup_x + pWindow->w() * f < 0) popup_x = - pWindow->w() * f; - if (menu_origin) { - popup_x -= menu_origin->x() * f; - popup_y -= menu_origin->y() * f; - } - if (popup_x >= origin_win->w() * f) popup_x = origin_win->w() * f - 1; - if (!Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_bartitle(pWindow) && - !Fl_Window_Driver::menu_leftorigin(pWindow)) { - // prevent first popup from going above the permissible source window - popup_y = fl_max(popup_y, - pWindow->h() * f); - } - if (parent_xid->kind == Fl_Wayland_Window_Driver::DECORATED && !origin_win->fullscreen_active()) - libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, - &popup_x, &popup_y); - xdg_positioner_set_anchor_rect(positioner, popup_x, 0, 1, 1); - popup_y++; - } - int positioner_H = pWindow->h(); - if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::KWIN) { - // Under KWIN, limiting the height of the positioner to the work area height - // results in tall popup windows starting at the top of the screen, what we want. - // Unfortunately, we know the work area height exactly only for single-screen systems, - // otherwise FLTK returns work area height == screen height. In that case we estimate - // work area height ≈ screen height - 44. - int V, work_area_H, screen_H; - Fl::screen_work_area(V, V, V, work_area_H, origin_win->screen_num()); - Fl::screen_xywh(V, V, V, screen_H, origin_win->screen_num()); - if (work_area_H == screen_H) work_area_H -= 44; - if (positioner_H > work_area_H) positioner_H = work_area_H; - } - xdg_positioner_set_size(positioner, pWindow->w() * f , positioner_H * f ); - xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT); - xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); - // prevent menuwindow from expanding beyond display limits - int constraint = 0; - int top_menubar = pWindow->y() - - (Fl_Window_Driver::menu_bartitle(pWindow) && Fl_Window_Driver::menu_title(pWindow) ? - Fl_Window_Driver::menu_title(pWindow)->h() : 0); - if ( !(parent_win->fullscreen_active() && - Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::MUTTER && - ((!Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_leftorigin(pWindow)) || - Fl_Window_Driver::menu_bartitle(pWindow)) && top_menubar < 10 && - !Fl_Window_Driver::current_menu_button) - ) { - // Condition above is only to bypass Mutter bug for fullscreen windows (see #1061) - constraint |= (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); - if ((Fl_Window_Driver::current_menu_button || Fl_Window_Driver::menu_bartitle(pWindow)) && - !Fl_Window_Driver::menu_leftorigin(pWindow)) { - constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; - } - xdg_positioner_set_constraint_adjustment(positioner, constraint); - } - if (!(Fl_Window_Driver::menu_title(pWindow) && Fl_Window_Driver::menu_bartitle(pWindow))) { - xdg_positioner_set_offset(positioner, 0, popup_y); - } - new_window->xdg_popup = xdg_surface_get_popup(new_window->xdg_surface, - parent_xdg, positioner); - struct win_positioner *win_pos = new struct win_positioner; - win_pos->window = new_window; - win_pos->x = popup_x; - win_pos->y = popup_y; - win_pos->child_popup = NULL; -//printf("create xdg_popup=%p data=%p xid=%p fl_win=%p\n",new_window->xdg_popup,win_pos,new_window,new_window->fl_win); - xdg_positioner_destroy(positioner); - xdg_popup_add_listener(new_window->xdg_popup, &popup_listener, win_pos); - if (!mem_grabbing_popup) { - mem_grabbing_popup = new_window->xdg_popup; - //xdg_popup_grab(new_window->xdg_popup, scr_driver->get_wl_seat(), scr_driver->get_serial()); - //libdecor_frame_popup_grab(parent_xid->frame, scr_driver->get_seat_name()); - } - wl_surface_commit(new_window->wl_surface); - // put it on same screen as parent_win - this->screen_num(parent_win->screen_num()); - return false; -} - - -void Fl_Wayland_Window_Driver::makeWindow() -{ - Fl_Group::current(0); // get rid of very common user bug: forgot end() - struct wld_window *new_window; - bool is_floatingtitle = false; - wait_for_expose_value = 1; - - if (pWindow->parent() && !pWindow->window()) return; - if (pWindow->parent() && !pWindow->window()->shown()) return; - - if (!pWindow->parent() && !popup_window()) { - x(0); y(0); // toplevel, non-popup windows must have origin at 0,0 - } - new_window = (struct wld_window *)calloc(1, sizeof *new_window); - new_window->fl_win = pWindow; - wl_list_init(&new_window->outputs); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - - new_window->wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor); -//printf("makeWindow:%p %s %s\n", pWindow, pWindow->parent()?"SUB":"", pWindow->as_gl_window()?"GL":""); - wl_surface_add_listener(new_window->wl_surface, &surface_listener, new_window); - - if (!shape()) { // rectangular FLTK windows are opaque - struct wl_region *opaque = wl_compositor_create_region(scr_driver->wl_compositor); - wl_region_add(opaque, 0, 0, 1000000, 1000000); - wl_surface_set_opaque_region(new_window->wl_surface, opaque); - wl_region_destroy(opaque); - } - - if (pWindow->user_data() == &Fl_Screen_Driver::transient_scale_display && - Fl::first_window()) { - // put transient scale win at center of top window by making it a tooltip of top - Fl_Screen_Driver::transient_scale_parent = Fl::first_window(); - pWindow->set_tooltip_window(); - set_popup_window(); - pWindow->position( - (Fl_Screen_Driver::transient_scale_parent->w() - pWindow->w())/2 , - (Fl_Screen_Driver::transient_scale_parent->h() - pWindow->h())/2); - } - - if (popup_window()) { // a menu window or tooltip - is_floatingtitle = process_menu_or_tooltip(new_window); - - } else if (pWindow->border() && !pWindow->parent() ) { // a decorated window - new_window->kind = DECORATED; - if (!scr_driver->libdecor_context) - scr_driver->libdecor_context = libdecor_new(Fl_Wayland_Screen_Driver::wl_display, - &libdecor_iface); - new_window->frame = libdecor_decorate(scr_driver->libdecor_context, new_window->wl_surface, - &libdecor_frame_iface, new_window); - // appears in the Gnome desktop menu bar - libdecor_frame_set_app_id(new_window->frame, get_prog_name()); - libdecor_frame_set_title(new_window->frame, pWindow->label()?pWindow->label():""); - if (!is_resizable()) { - libdecor_frame_unset_capabilities(new_window->frame, LIBDECOR_ACTION_RESIZE); - libdecor_frame_unset_capabilities(new_window->frame, LIBDECOR_ACTION_FULLSCREEN); - } - libdecor_frame_map(new_window->frame); - float f = Fl::screen_scale(pWindow->screen_num()); - new_window->floating_width = pWindow->w() * f; - new_window->floating_height = pWindow->h() * f; - - } else if (pWindow->parent()) { // for subwindows (GL or non-GL) - new_window->kind = SUBWINDOW; - struct wld_window *parent = fl_wl_xid(pWindow->window()); - new_window->subsurface = wl_subcompositor_get_subsurface(scr_driver->wl_subcompositor, - new_window->wl_surface, - parent->wl_surface); -//fprintf(stderr, "makeWindow: subsurface=%p\n", new_window->subsurface); - float f = Fl::screen_scale(pWindow->top_window()->screen_num()); - wl_subsurface_set_position(new_window->subsurface, pWindow->x() * f, pWindow->y() * f); - wl_subsurface_set_desync(new_window->subsurface); // important - // Next 5 statements ensure the subsurface will be mapped because: - // "The effect of adding a sub-surface becomes visible on the next time - // the state of the parent surface is applied." - new_window->configured_width = pWindow->w(); - new_window->configured_height = pWindow->h(); - if (!pWindow->as_gl_window()) { - parent->fl_win->wait_for_expose(); - wl_surface_commit(parent->wl_surface); - } - wait_for_expose_value = 0; - pWindow->border(0); - checkSubwindowFrame(); // make sure subwindow doesn't leak outside parent - - } else { // a window without decoration - new_window->kind = UNFRAMED; - new_window->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, - new_window->wl_surface); -//fprintf(stderr, "makeWindow: xdg_wm_base_get_xdg_surface=%p\n", new_window->xdg_surface); - xdg_surface_add_listener(new_window->xdg_surface, &xdg_surface_listener, new_window); - new_window->xdg_toplevel = xdg_surface_get_toplevel(new_window->xdg_surface); - xdg_toplevel_add_listener(new_window->xdg_toplevel, &xdg_toplevel_listener, new_window); - if (pWindow->label()) xdg_toplevel_set_title(new_window->xdg_toplevel, pWindow->label()); - wl_surface_commit(new_window->wl_surface); - pWindow->border(0); - } - - Fl_Window *old_first = Fl::first_window(); - struct wld_window * first_xid = (old_first ? fl_wl_xid(old_first) : NULL); - Fl_X *xp = new Fl_X; - xp->xid = (fl_uintptr_t)new_window; - other_xid = 0; - xp->w = pWindow; - flx(xp); - xp->region = 0; - if (!pWindow->parent()) { - xp->next = Fl_X::first; - Fl_X::first = xp; - } else if (Fl_X::first) { - xp->next = Fl_X::first->next; - Fl_X::first->next = xp; - } else { - xp->next = NULL; - Fl_X::first = xp; - } - - if (pWindow->modal() || pWindow->non_modal()) { - if (pWindow->modal()) Fl::modal_ = pWindow; - if (new_window->kind == DECORATED && first_xid && first_xid->kind == DECORATED) { - if (first_xid->frame) libdecor_frame_set_parent(new_window->frame, first_xid->frame); - } else if (new_window->kind == UNFRAMED && new_window->xdg_toplevel && first_xid) { - Fl_Wayland_Window_Driver *top_dr = Fl_Wayland_Window_Driver::driver(first_xid->fl_win); - if (top_dr->xdg_toplevel()) xdg_toplevel_set_parent(new_window->xdg_toplevel, - top_dr->xdg_toplevel()); - } - if (new_window->kind == DECORATED || new_window->kind == UNFRAMED) { -#if HAVE_XDG_DIALOG - if (scr_driver->xdg_wm_dialog) { - new_window->xdg_dialog = xdg_wm_dialog_v1_get_xdg_dialog(scr_driver->xdg_wm_dialog, xdg_toplevel()); - if (pWindow->modal()) xdg_dialog_v1_set_modal(new_window->xdg_dialog); - } else -#endif - if (scr_driver->seat->gtk_shell && pWindow->modal()) { - // Useful to position modal windows above their parent with "gnome-shell --version" ≤ 45.2, - // useless but harmless with "gnome-shell --version" ≥ 46.0. - struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(scr_driver->seat->gtk_shell, - new_window->wl_surface); - gtk_surface1_set_modal(gtk_surface); - if (gtk_surface1_get_version(gtk_surface) >= GTK_SURFACE1_RELEASE_SINCE_VERSION) - gtk_surface1_release(gtk_surface); // very necessary - else - gtk_surface1_destroy(gtk_surface); - } - } - } - - size_range(); - pWindow->set_visible(); - int old_event = Fl::e_number; - pWindow->redraw(); - pWindow->handle(Fl::e_number = FL_SHOW); // get child windows to appear - Fl::e_number = old_event; - if (pWindow->menu_window() && popup_window() && !is_floatingtitle) { - // make sure each menu window is mapped with its constraints before mapping next popup - pWindow->wait_for_expose(); - if (previous_floatingtitle) { // a menuwindow with a menutitle - //puts("previous_floatingtitle"); - int HH; - Fl_Window_Driver::menu_parent(&HH); - if (pWindow->h() > HH) { - // a tall menuwindow with a menutitle: don't create the menutitle at all - // and undo what has been created/allocated before - struct wld_window *xid = fl_wl_xid(previous_floatingtitle); - destroy_surface_caution_pointer_focus(xid->wl_surface, scr_driver->seat); - free(xid); - Fl_Window_Driver::driver(previous_floatingtitle)->hide_common(); - previous_floatingtitle = NULL; - return; - } - // map the menutitle popup now as child of pWindow - struct wld_window *xid = fl_wl_xid(previous_floatingtitle); - xid->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, xid->wl_surface); - xdg_surface_add_listener(xid->xdg_surface, &xdg_surface_listener, xid); - struct xdg_positioner *positioner = - xdg_wm_base_create_positioner(scr_driver->xdg_wm_base); - xdg_positioner_set_anchor_rect(positioner, 0, 0, 1, 1); - int snum = Fl_Window_Driver::menu_parent()->screen_num(); - float f = Fl::screen_scale(snum); - // put it on same screen as parent menu - Fl_Window_Driver::driver(previous_floatingtitle)->screen_num(snum); - xdg_positioner_set_size(positioner, previous_floatingtitle->w() * f , - previous_floatingtitle->h() * f ); - xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); - xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_TOP_RIGHT); - xid->xdg_popup = xdg_surface_get_popup(xid->xdg_surface, new_window->xdg_surface, - positioner); - xdg_positioner_destroy(positioner); - struct win_positioner *win_pos = new struct win_positioner; - win_pos->window = xid; - win_pos->x = 0; - win_pos->y = 0; - win_pos->child_popup = NULL; - xdg_popup_add_listener(xid->xdg_popup, &popup_listener, win_pos); - wl_surface_commit(xid->wl_surface); - struct win_positioner *parent_win_pos = - (struct win_positioner*)xdg_popup_get_user_data(new_window->xdg_popup); - parent_win_pos->child_popup = previous_floatingtitle; - previous_floatingtitle = NULL; - } - } - if (pWindow->fullscreen_active()) Fl::handle(FL_FULLSCREEN, pWindow); -} - - -int Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor c) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow); -#if HAVE_CURSOR_SHAPE - if (scr_driver->wp_cursor_shape_device) { - if (xid->custom_cursor) { - delete_cursor(xid->custom_cursor); - xid->custom_cursor = NULL; - } - if (c == FL_CURSOR_NONE) return 0; - standard_cursor_ = c; - Fl_Wayland_Screen_Driver::do_set_cursor(scr_driver->seat, NULL, c); - return 1; - } -#endif // HAVE_CURSOR_SHAPE - if (!scr_driver->seat->cursor_theme) return 1; - // Cursor names are the files of directory /usr/share/icons/XXXX/cursors/ - // where XXXX is the name of the current 'cursor theme'. - static struct cursor_file_struct { - Fl_Cursor c; - const char *fname; - Fl_Wayland_Screen_Driver::cursor_shapes wld_c; - } cursor_file_array[] = { - {FL_CURSOR_ARROW, "left_ptr", Fl_Wayland_Screen_Driver::arrow }, - {FL_CURSOR_CROSS, "cross", Fl_Wayland_Screen_Driver::cross }, - {FL_CURSOR_WAIT, "watch", Fl_Wayland_Screen_Driver::wait }, - {FL_CURSOR_INSERT, "xterm", Fl_Wayland_Screen_Driver::insert }, - {FL_CURSOR_HAND, "hand1", Fl_Wayland_Screen_Driver::hand }, - {FL_CURSOR_HELP, "help", Fl_Wayland_Screen_Driver::help }, - {FL_CURSOR_MOVE, "move", Fl_Wayland_Screen_Driver::move }, - {FL_CURSOR_N, "top_side", Fl_Wayland_Screen_Driver::north }, - {FL_CURSOR_E, "right_side", Fl_Wayland_Screen_Driver::east }, - {FL_CURSOR_W, "left_side", Fl_Wayland_Screen_Driver::west }, - {FL_CURSOR_S, "bottom_side", Fl_Wayland_Screen_Driver::south }, - {FL_CURSOR_NS, "sb_v_double_arrow", Fl_Wayland_Screen_Driver::north_south }, - {FL_CURSOR_WE, "sb_h_double_arrow", Fl_Wayland_Screen_Driver::west_east }, - {FL_CURSOR_SW, "bottom_left_corner", Fl_Wayland_Screen_Driver::south_west }, - {FL_CURSOR_SE, "bottom_right_corner", Fl_Wayland_Screen_Driver::south_east }, - {FL_CURSOR_NE, "top_right_corner", Fl_Wayland_Screen_Driver::north_east }, - {FL_CURSOR_NW, "top_left_corner", Fl_Wayland_Screen_Driver::north_west }, - {FL_CURSOR_NESW, "fd_double_arrow", Fl_Wayland_Screen_Driver::nesw }, - {FL_CURSOR_NWSE, "bd_double_arrow", Fl_Wayland_Screen_Driver::nwse } - }; - - int found = -1; - for (unsigned i = 0; i < sizeof(cursor_file_array) / sizeof(struct cursor_file_struct); i++) { - if (cursor_file_array[i].c == c) { - found = cursor_file_array[i].wld_c; - if (!scr_driver->xc_cursor[found]) scr_driver->xc_cursor[found] = - scr_driver->cache_cursor(cursor_file_array[i].fname); - if (scr_driver->xc_cursor[found]) { - scr_driver->default_cursor(scr_driver->xc_cursor[found]); - } - break; - } - } - if (found < 0 || !scr_driver->xc_cursor[found]) return 0; - - if (xid->custom_cursor) { - delete_cursor(xid->custom_cursor); - xid->custom_cursor = NULL; - } - standard_cursor_ = c; - scr_driver->set_cursor(); - return 1; -} - - -void Fl_Wayland_Window_Driver::use_border() { - if (!shown() || pWindow->parent()) return; - pWindow->wait_for_expose(); // useful for border(0) just after show() - struct libdecor_frame *frame = fl_wl_xid(pWindow)->frame; - if (frame && Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::KWIN) { - if (fl_wl_xid(pWindow)->kind == DECORATED) { - libdecor_frame_set_visibility(frame, pWindow->border()); - } else { - pWindow->hide(); - pWindow->show(); - } - pWindow->redraw(); - } else { - Fl_Window_Driver::use_border(); - } -} - - -/* Change an existing window to fullscreen */ -void Fl_Wayland_Window_Driver::fullscreen_on() { - int top, bottom, left, right; - - top = fullscreen_screen_top(); - bottom = fullscreen_screen_bottom(); - left = fullscreen_screen_left(); - right = fullscreen_screen_right(); - - if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) { - top = screen_num(); - bottom = top; - left = top; - right = top; - } - pWindow->wait_for_expose(); // make sure ->xdg_toplevel is initialized - if (xdg_toplevel()) { - xdg_toplevel_set_fullscreen(xdg_toplevel(), NULL); - pWindow->_set_fullscreen(); - Fl::handle(FL_FULLSCREEN, pWindow); - } -} - - -void Fl_Wayland_Window_Driver::fullscreen_off(int X, int Y, int W, int H) { - pWindow->hide(); - pWindow->_clear_fullscreen(); - // avoid being called with W=H=0 in suboptimal scenario of #1299 - if (!W) W = w(); - if (!H) H = h(); - pWindow->resize(X, Y, W, H); - pWindow->show(); - Fl::handle(FL_FULLSCREEN, pWindow); -} - - -void Fl_Wayland_Window_Driver::label(const char *name, const char *iname) { - if (shown() && !parent() && fl_wl_xid(pWindow)->kind == DECORATED) { - if (!name) name = ""; - if (!iname) iname = fl_filename_name(name); - libdecor_frame_set_title(fl_wl_xid(pWindow)->frame, name); - } -} - - -int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty) { - int retval = set_cursor_4args(rgb, hotx, hoty, true); - if (retval) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow); - Fl_Wayland_Screen_Driver::do_set_cursor(scr_driver->seat, xid->custom_cursor->wl_cursor); - } - return retval; -} - - -int Fl_Wayland_Window_Driver::set_cursor_4args(const Fl_RGB_Image *rgb, int hotx, int hoty, - bool keep_copy) { - if (keep_copy) { - if (rgb->as_svg_image()) { - int scale = wld_scale(); - Fl_RGB_Image *svg = (Fl_RGB_Image*)rgb->copy(rgb->w() * scale, rgb->h() * scale); - svg->normalize(); - svg->scale(rgb->w(), rgb->h(), 0, 1); - rgb = svg; - } else { - int ld = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d(); - uchar *data = new uchar[ld * rgb->data_h()]; - memcpy(data, rgb->array, ld * rgb->data_h()); - Fl_RGB_Image *rgb2 = new Fl_RGB_Image(data, rgb->data_w(), rgb->data_h(), rgb->d(), rgb->ld()); - rgb2->alloc_array = 1; - rgb2->scale(rgb->w(), rgb->h(), 0, 1); - rgb = rgb2; - } - } -// build a new wl_cursor and its image - struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow); - struct wl_cursor *new_cursor = (struct wl_cursor*)malloc(sizeof(struct wl_cursor)); - struct cursor_image *new_image = (struct cursor_image*)calloc(1, - sizeof(struct cursor_image)); - int scale = wld_scale(); - new_image->image.width = rgb->w() * scale; - new_image->image.height = rgb->h() * scale; - new_image->image.hotspot_x = hotx * scale; - new_image->image.hotspot_y = hoty * scale; - new_image->image.delay = 0; - new_image->offset = 0; - //create a Wayland buffer and have it used as an image of the new cursor - struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen; - Fl_Image_Surface *img_surf = Fl_Wayland_Graphics_Driver::custom_offscreen( - new_image->image.width, new_image->image.height, &offscreen); - new_image->buffer = offscreen->wl_buffer; - wl_buffer_set_user_data(new_image->buffer, offscreen); - new_cursor->image_count = 1; - new_cursor->images = (struct wl_cursor_image**)malloc(sizeof(struct wl_cursor_image*)); - new_cursor->images[0] = (struct wl_cursor_image*)new_image; - new_cursor->name = strdup("custom cursor"); - // draw the rgb image to the cursor's drawing buffer - Fl_Surface_Device::push_current(img_surf); - Fl_Wayland_Graphics_Driver *driver = (Fl_Wayland_Graphics_Driver*)img_surf->driver(); - cairo_scale(driver->cr(), scale, scale); - ((Fl_RGB_Image*)rgb)->draw(0, 0); - Fl_Surface_Device::pop_current(); - delete img_surf; - memcpy(offscreen->data, offscreen->draw_buffer.buffer, offscreen->draw_buffer.data_size); - // delete the previous custom cursor, if there was one, - // and keep its Fl_RGB_Image if appropriate - if (xid->custom_cursor) delete_cursor(xid->custom_cursor, keep_copy); - //have this new cursor used - xid->custom_cursor = new custom_cursor; - xid->custom_cursor->wl_cursor = new_cursor; - xid->custom_cursor->rgb = rgb; - xid->custom_cursor->hotx = hotx; - xid->custom_cursor->hoty = hoty; - return 1; -} - - -void Fl_Wayland_Window_Driver::resize(int X, int Y, int W, int H) { - static int depth = 0; - struct wld_window *fl_win = fl_wl_xid(pWindow); - if (fl_win && fl_win->kind == DECORATED && !xdg_toplevel()) { - pWindow->wait_for_expose(); - } - int is_a_move = (X != x() || Y != y()); - bool true_rescale = Fl_Window::is_a_rescale(); - float f = fl_win ? Fl::screen_scale(pWindow->screen_num()) : 1; - if (fl_win && fl_win->buffer) { - int scale = wld_scale(); - int stride = cairo_format_stride_for_width( - Fl_Cairo_Graphics_Driver::cairo_format, int(W * f) * scale ); - size_t bsize = stride * int(H * f) * scale; - true_rescale = (bsize != fl_win->buffer->draw_buffer.data_size); - } - int is_a_resize = (W != w() || H != h() || true_rescale); - if (is_a_move) force_position(1); - else if (!is_a_resize && !is_a_move) return; - depth++; - if (shown() && !(parent() || popup_window())) { - X = Y = 0; - } - Fl_Window *parent = this->parent() ? pWindow->window() : NULL; - struct wld_window *parent_xid = parent ? fl_wl_xid(parent) : NULL; -//printf("resize[%p] %dx%d is_a_resize=%d is_a_move=%d depth=%d parent_xid->frame_cb=%p\n", pWindow,W,H,is_a_resize,is_a_move,depth, (parent_xid?parent_xid->frame_cb:0) ); - if (depth == 1 && fl_win && parent_xid && parent_xid->frame_cb && can_expand_outside_parent_) { - // When moving or resizing a subwindow independently from its parent while the parent window - // is being redrawn, the processing depends on whether the moved/resize window - // is a draggable-subwindow. For a draggable subwindow having can_expand_outside_parent_ != 0, - // skip the X,Y,W,H tuple to process only tuples received when parent window is ready. - // This smoothes the movement of the draggable subwindow. - // Process regular subwindows normally. - depth--; - return; - } - if (is_a_resize) { - if (pWindow->parent()) { - if (W < 1) W = 1; - if (H < 1) H = 1; - } - pWindow->Fl_Group::resize(X,Y,W,H); - //fprintf(stderr, "resize: win=%p to %dx%d\n", pWindow, W, H); - if (shown()) {pWindow->redraw();} - } else { - x(X); y(Y); - //fprintf(stderr, "move win=%p to %dx%d\n", pWindow, X, Y); - } - if (!fl_win) { - depth--; - return; - } - - if (is_a_resize) { - if (pWindow->as_overlay_window() && other_xid) { - destroy_double_buffer(); - } - if (fl_win->kind == DECORATED) { // a decorated window - if (fl_win->buffer) { - Fl_Wayland_Graphics_Driver::buffer_release(fl_win); - } - fl_win->configured_width = W; - fl_win->configured_height = H; - if (!in_handle_configure && xdg_toplevel()) { - if (Fl_Window::is_a_rescale()) size_range(); - struct libdecor_state *state = libdecor_state_new(int(W * f), int(H * f)); - // necessary only if resize is initiated by prog - libdecor_frame_commit(fl_win->frame, state, NULL); - libdecor_state_free(state); - if (libdecor_frame_is_floating(fl_win->frame)) { - fl_win->floating_width = int(W*f); - fl_win->floating_height = int(H*f); - } - } - } else if (fl_win->kind == SUBWINDOW && fl_win->subsurface) { // a subwindow - wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f); - if (!pWindow->as_gl_window()) Fl_Wayland_Graphics_Driver::buffer_release(fl_win); - fl_win->configured_width = W; - fl_win->configured_height = H; - } else if (fl_win->xdg_surface) { // a window without border - if (!pWindow->as_gl_window()) Fl_Wayland_Graphics_Driver::buffer_release(fl_win); - fl_win->configured_width = W; - fl_win->configured_height = H; - W *= f; H *= f; - xdg_surface_set_window_geometry(fl_win->xdg_surface, 0, 0, W, H); - //printf("xdg_surface_set_window_geometry: %dx%d\n",W, H); - } - } else if (!in_handle_configure && xdg_toplevel() && Fl::e_state == FL_BUTTON1) { - // Wayland doesn't provide a way for the app to set the window position on screen. - // This is functional when the move is mouse-driven. - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - xdg_toplevel_move(xdg_toplevel(), scr_driver->seat->wl_seat, scr_driver->seat->serial); - Fl::pushed(NULL); - Fl::e_state = 0; - } - - if (parent_xid) { - if (depth > 1) { - if (fl_win->subsurface) { - wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f); - wl_surface_commit(parent_xid->wl_surface); - } - } else if (parent_xid->buffer && is_a_move) { - if (fl_win->subsurface) wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f); - if (!parent_xid->buffer->wl_buffer || parent_xid->buffer->draw_buffer_needs_commit) { - if (!parent_xid->frame_cb) Fl_Wayland_Graphics_Driver::buffer_commit(parent_xid); - else wl_surface_commit(parent_xid->wl_surface); - } else { - if (!parent_xid->frame_cb) { - // Use the frame callback mechanism applied to the object's parent window - parent_xid->frame_cb = wl_surface_frame(parent_xid->wl_surface); - wl_callback_add_listener(parent_xid->frame_cb, - Fl_Wayland_Graphics_Driver::p_surface_frame_listener, parent_xid); - } - wl_surface_commit(parent_xid->wl_surface); - } - } - checkSubwindowFrame(); // make sure subwindow doesn't leak outside parent - } - depth--; -} - - -static void crect_intersect(cairo_rectangle_int_t *to, cairo_rectangle_int_t *with) { - int x = fl_max(to->x, with->x); - to->width = fl_min(to->x + to->width, with->x + with->width) - x; - if (to->width < 0) to->width = 0; - int y = fl_max(to->y, with->y); - to->height = fl_min(to->y + to->height, with->y + with->height) - y; - if (to->height < 0) to->height = 0; - to->x = x; - to->y = y; -} - - -static bool crect_equal(cairo_rectangle_int_t *to, cairo_rectangle_int_t *with) { - return (to->x == with->x && to->y == with->y && to->width == with->width && - to->height == with->height); -} - - -void Fl_Wayland_Window_Driver::checkSubwindowFrame() { - if (!pWindow->parent() || can_expand_outside_parent_) return; - // make sure this subwindow doesn't leak out of its parent window - Fl_Window *from = pWindow, *parent; - cairo_rectangle_int_t full = {0, 0, pWindow->w(), pWindow->h()}; // full subwindow area - cairo_rectangle_int_t srect = full; // will become new subwindow clip - int fromx = 0, fromy = 0; - while ((parent = from->window()) != NULL) { // loop over all parent windows - fromx -= from->x(); // parent origin in subwindow's coordinates - fromy -= from->y(); - cairo_rectangle_int_t prect = {fromx, fromy, parent->w(), parent->h()}; - crect_intersect(&srect, &prect); // area of subwindow inside its parent - from = parent; - } - cairo_rectangle_int_t *r = subRect(); - // current subwindow clip - cairo_rectangle_int_t current_clip = (r ? *r : full); - if (!crect_equal(&srect, ¤t_clip)) { // if new clip differs from current clip - if (crect_equal(&srect, &full)) r = NULL; - else { - r = &srect; - if (r->width == 0 || r->height == 0) { - r = NULL; - } - } - subRect(r); - } -} - - -void Fl_Wayland_Window_Driver::subRect(cairo_rectangle_int_t *r) { - if (subRect_) delete subRect_; - cairo_rectangle_int_t *r2 = NULL; - if (r) { - r2 = new cairo_rectangle_int_t; - *r2 = *r; - } - subRect_ = r2; -} - - -void Fl_Wayland_Window_Driver::reposition_menu_window(int x, int y) { - if (y == pWindow->y()) return; - // The top of the tall popup window was positioned at the top of the screen - // Instead of sliding up the popup window on the display, we slide up the - // drawing inside the fixed popup via member variable offset_y of the - // menuwindow class, and we redraw the popup content. - // It's also useful to make such tall popup window transparent. - *Fl_Window_Driver::menu_offset_y(pWindow) += (y - pWindow->y()); - struct wld_window *xid = fl_wl_xid(pWindow); - wl_surface_set_opaque_region(xid->wl_surface, NULL); - if (xid->buffer) memset(xid->buffer->draw_buffer.buffer, 0, - xid->buffer->draw_buffer.data_size); - //printf("offset_y=%d\n", *Fl_Window_Driver::menu_offset_y(pWindow)); - this->y(y); - pWindow->redraw(); -} - - -void Fl_Wayland_Window_Driver::menu_window_area(int &X, int &Y, int &W, int &H, int nscreen) { - int HH; - Fl_Window *parent = Fl_Window_Driver::menu_parent(&HH); - if (parent) { - if (pWindow->menu_window() && popup_window() && pWindow->h() > HH) { - // tall menu: set top (Y) and bottom (Y+H) bounds relatively to reference window - int ih = Fl_Window_Driver::menu_itemheight(pWindow); - X = -50000; - W = 1000000; - H = HH - 2 * ih; - Fl_Window *origin = Fl_Window_Driver::menu_leftorigin(pWindow); - if (origin) { // has left parent - int selected = fl_max(Fl_Window_Driver::menu_selected(origin), 0); - Y = origin->y() + (selected + 0.5) * ih; - } else if (!Fl_Window_Driver::menu_bartitle(pWindow)) { // tall menu button - static int y_offset = 0; - if (new_popup) { - y_offset = pWindow->y()- ih; - new_popup = false; - } - Y = 1.5 * ih + y_offset; - } else { // has a menutitle - Y = 1.5 * ih; - } - } else { // position the menu window by wayland constraints - X = -50000; - Y = -50000; - W = 1000000; - H = 1000000; - } - //printf("menu_window_area: %dx%d - %dx%d\n",X,Y,W,H); - } else Fl_Window_Driver::menu_window_area(X, Y, W, H, nscreen); -} - - -int Fl_Wayland_Window_Driver::wld_scale() { - Fl_X *flx = Fl_X::flx(pWindow); - struct wld_window *xid = (flx ? (struct wld_window *)flx->xid : NULL); - if (!xid || wl_list_empty(&xid->outputs)) { - int scale = 1; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &(scr_driver->outputs), link) { - scale = fl_max(scale, output->wld_scale); - } - return scale; - } - struct surface_output *s_output; - s_output = wl_container_of(xid->outputs.next, s_output, link); - return s_output->output->wld_scale; -} - - -FL_EXPORT struct wl_surface *fl_wl_surface(struct wld_window *xid) { - return xid->wl_surface; -} - - -cairo_t *fl_wl_gc() { - return ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->cr(); -} - - -Fl_Window *fl_wl_find(struct wld_window *xid) { - return Fl_Window_Driver::find((fl_uintptr_t)xid); -} - - -struct wld_window *fl_wl_xid(const Fl_Window *win) { - return (struct wld_window *)Fl_Window_Driver::xid(win); -} - - -struct wl_compositor *fl_wl_compositor() { - Fl_Wayland_Screen_Driver *screen_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - return screen_driver->wl_compositor; -} - - -int fl_wl_buffer_scale(Fl_Window *window) { - return Fl_Wayland_Window_Driver::driver(window)->wld_scale(); -} - - -Fl_Wayland_Plugin *Fl_Wayland_Window_Driver::gl_plugin() { - static Fl_Wayland_Plugin *plugin = NULL; - if (!plugin) { - Fl_Plugin_Manager pm("wayland.fltk.org"); - plugin = (Fl_Wayland_Plugin*)pm.plugin("gl.wayland.fltk.org"); - } - return plugin; -} - - -void Fl_Wayland_Window_Driver::maximize() { - struct wld_window *xid = (struct wld_window *)Fl_X::flx(pWindow)->xid; - if (xid->kind == DECORATED) libdecor_frame_set_maximized(xid->frame); - else Fl_Window_Driver::maximize(); -} - - -void Fl_Wayland_Window_Driver::un_maximize() { - struct wld_window *xid = (struct wld_window *)Fl_X::flx(pWindow)->xid; - if (xid->kind == DECORATED) libdecor_frame_unset_maximized(xid->frame); - else Fl_Window_Driver::un_maximize(); -} diff --git a/src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx b/src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx deleted file mode 100644 index 12c525c46..000000000 --- a/src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx +++ /dev/null @@ -1,741 +0,0 @@ -// -// Wayland-specific code for clipboard and drag-n-drop support. -// -// Copyright 1998-2026 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 -// - -#if !defined(FL_DOXYGEN) - -# include <FL/Fl.H> -# include <FL/platform.H> -# include <FL/Fl_Window.H> -# include <FL/Fl_Shared_Image.H> -# include <FL/Fl_Image_Surface.H> -# include "Fl_Wayland_Screen_Driver.H" -# include "Fl_Wayland_Window_Driver.H" -# include "../Unix/Fl_Unix_System_Driver.H" -# include "Fl_Wayland_Graphics_Driver.H" -# include "../../flstring.h" // includes <string.h> - -# include <errno.h> -# include <stdio.h> -# include <stdlib.h> -# include <map> - - -//////////////////////////////////////////////////////////////// -// Code used for copy and paste and DnD into the program: - -static char *fl_selection_buffer[2]; -static int fl_selection_length[2]; -static const char * fl_selection_type[2]; -static int fl_selection_buffer_length[2]; -static char fl_i_own_selection[2] = {0,0}; -static struct wl_data_offer *fl_selection_offer = NULL; -// The MIME type Wayland uses for text-containing clipboard: -static const char wld_plain_text_clipboard[] = "text/plain;charset=utf-8"; - - -int Fl_Wayland_Screen_Driver::clipboard_contains(const char *type) -{ - return fl_selection_type[1] == type; -} - - -struct data_source_write_struct { - size_t rest; - char *from; -}; - -void write_data_source_cb(FL_SOCKET fd, data_source_write_struct *data) { - while (data->rest) { - ssize_t n = write(fd, data->from, data->rest); - if (n == -1) { - if (errno == EAGAIN) return; - Fl::error("write_data_source_cb: error while writing clipboard data\n"); - break; - } - data->from += n; - data->rest -= n; - } - Fl::remove_fd(fd, FL_WRITE); - delete data; - close(fd); -} - - -static void data_source_handle_send(void *data, struct wl_data_source *source, - const char *mime_type, int fd) { - fl_intptr_t rank = (fl_intptr_t)data; -//fprintf(stderr, "data_source_handle_send: %s fd=%d l=%d\n", mime_type, fd, fl_selection_length[1]); - if (((!strcmp(mime_type, wld_plain_text_clipboard) || !strcmp(mime_type, "text/plain")) && - fl_selection_type[rank] == Fl::clipboard_plain_text) - || - (!strcmp(mime_type, "image/bmp") && fl_selection_type[rank] == Fl::clipboard_image) ) { - data_source_write_struct *write_data = new data_source_write_struct; - write_data->rest = fl_selection_length[rank]; - write_data->from = fl_selection_buffer[rank]; - Fl::add_fd(fd, FL_WRITE, (Fl_FD_Handler)write_data_source_cb, write_data); - } else { - //Fl::error("Destination client requested unsupported MIME type: %s\n", mime_type); - close(fd); - } -} - - -static Fl_Window *fl_dnd_target_window = 0; -static wl_surface *fl_dnd_target_surface = 0; -static bool doing_dnd = false; // true when DnD is in action -static wl_surface *dnd_icon = NULL; // non null when DnD uses text as cursor -static wl_cursor* save_cursor = NULL; // non null when DnD uses "dnd-copy" cursor - - -static void data_source_handle_cancelled(void *data, struct wl_data_source *source) { - // An application has replaced the clipboard contents or DnD finished - wl_data_source_destroy(source); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->seat->data_source == source) scr_driver->seat->data_source = NULL; - doing_dnd = false; - if (dnd_icon) { - struct Fl_Wayland_Graphics_Driver::wld_buffer *off = - (struct Fl_Wayland_Graphics_Driver::wld_buffer *) - wl_surface_get_user_data(dnd_icon); - struct wld_window fake_window; - memset(&fake_window, 0, sizeof(fake_window)); - fake_window.buffer = off; - Fl_Wayland_Graphics_Driver::buffer_release(&fake_window); - wl_surface_destroy(dnd_icon); - dnd_icon = NULL; - } - fl_i_own_selection[1] = 0; - if (data == 0) { // at end of DnD - if (save_cursor) { - scr_driver->default_cursor(save_cursor); - scr_driver->set_cursor(); - save_cursor = NULL; - } - if (fl_dnd_target_window) { - Fl::handle(FL_RELEASE, fl_dnd_target_window); - fl_dnd_target_window = 0; - } - Fl::pushed(0); - } -} - - -static void data_source_handle_target(void *data, struct wl_data_source *source, const char *mime_type) { - if (!Fl::pushed()) { - data_source_handle_cancelled(data, source); - return; - } - if (mime_type != NULL) { - //printf("Destination would accept MIME type if dropped: %s\n", mime_type); - } else { - //printf("Destination would reject if dropped\n"); - } -} - - -static uint32_t last_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - - -static void data_source_handle_action(void *data, struct wl_data_source *source, - uint32_t dnd_action) { - last_dnd_action = dnd_action; - switch (dnd_action) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - //printf("Destination would perform a copy action if dropped\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: - //printf("Destination would reject the drag if dropped\n"); - break; - } -} - - -static void data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *source) { - //printf("Drop performed\n"); -} - - -static void data_source_handle_dnd_finished(void *data, struct wl_data_source *source) { - switch (last_dnd_action) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: - //printf("Destination has accepted the drop with a move action\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - //printf("Destination has accepted the drop with a copy action\n"); - break; - } -} - - -static const struct wl_data_source_listener data_source_listener = { - .target = data_source_handle_target, - .send = data_source_handle_send, - .cancelled = data_source_handle_cancelled, - .dnd_drop_performed = data_source_handle_dnd_drop_performed, - .dnd_finished = data_source_handle_dnd_finished, - .action = data_source_handle_action, -}; - - -static struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen_from_text(const char *text, - int scale) { - const char *p, *q; - int width = 0, height, w2, ltext = strlen(text); - fl_font(FL_HELVETICA, 10 * scale); - p = text; - int nl = 0; - while(nl < 20 && (q=strchr(p, '\n')) != NULL) { - nl++; - w2 = int(fl_width(p, q - p)); - if (w2 > width) width = w2; - p = q + 1; - } - if (nl < 20 && text[ ltext - 1] != '\n') { - nl++; - w2 = int(fl_width(p)); - if (w2 > width) width = w2; - } - if (width > 300*scale) width = 300*scale; - height = nl * fl_height() + 3; - width += 6; - width = ceil(width/float(scale)) * scale; // these must be multiples of scale - height = ceil(height/float(scale)) * scale; - struct Fl_Wayland_Graphics_Driver::wld_buffer *off; - Fl_Image_Surface *surf = Fl_Wayland_Graphics_Driver::custom_offscreen( - width, height, &off); - Fl_Surface_Device::push_current(surf); - p = text; - fl_font(FL_HELVETICA, 10 * scale); - int y = fl_height(); - while (nl > 0) { - q = strchr(p, '\n'); - if (q) { - fl_draw(p, q - p, 3, y); - } else { - fl_draw(p, 3, y); - break; - } - y += fl_height(); - p = q + 1; - nl--; - } - Fl_Surface_Device::pop_current(); - delete surf; - cairo_surface_flush( cairo_get_target(off->draw_buffer.cairo_) ); - memcpy(off->data, off->draw_buffer.buffer, off->draw_buffer.data_size); - return off; -} - - -int Fl_Wayland_Screen_Driver::dnd(int use_selection) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - - struct wl_data_source *source = - wl_data_device_manager_create_data_source(scr_driver->seat->data_device_manager); - // we transmit the adequate value of index in fl_selection_buffer[index] - wl_data_source_add_listener(source, &data_source_listener, (void*)0); - wl_data_source_offer(source, wld_plain_text_clipboard); - wl_data_source_set_actions(source, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); - struct Fl_Wayland_Graphics_Driver::wld_buffer *off = NULL; - int s = 1; - if (use_selection) { - // use the text as dragging icon - Fl_Widget *current = Fl::pushed() ? Fl::pushed() : Fl::first_window(); - s = Fl_Wayland_Window_Driver::driver(current->top_window())->wld_scale(); - off = (struct Fl_Wayland_Graphics_Driver::wld_buffer *)offscreen_from_text(fl_selection_buffer[0], s); - dnd_icon = wl_compositor_create_surface(scr_driver->wl_compositor); - } else dnd_icon = NULL; - doing_dnd = true; - wl_data_device_start_drag(scr_driver->seat->data_device, source, - scr_driver->seat->pointer_focus, dnd_icon, - scr_driver->seat->serial); - if (use_selection) { - wl_surface_attach(dnd_icon, off->wl_buffer, 0, 0); - wl_surface_set_buffer_scale(dnd_icon, s); - wl_surface_damage(dnd_icon, 0, 0, 10000, 10000); - wl_surface_commit(dnd_icon); - wl_surface_set_user_data(dnd_icon, off); - } else { - static struct wl_cursor *dnd_cursor = scr_driver->cache_cursor("dnd-copy"); - if (dnd_cursor) { - save_cursor = scr_driver->default_cursor(); - scr_driver->default_cursor(dnd_cursor); - scr_driver->set_cursor(); - } else save_cursor = NULL; - } - return 1; -} - - -struct compare_utf8 { // used as key_comp member of following map object - bool operator()(const char *a, const char *b) const { return strcmp(a, b) < 0; } -}; - -// map: for each clipboard mime-type FLTK has interest in, give FLTK clipboard type and priority. -// A mime-type with higher priority for same FLTK clipboard type is preferred. -typedef struct { const char * const fltk_type; int priority; } type_prio_struct; -static std::map<const char * const, type_prio_struct, compare_utf8> clipboard_mimetypes_map { -// mime-type FLTK-clipboard-type priority - {"image/png", {Fl::clipboard_image, 1} }, - {"image/bmp", {Fl::clipboard_image, 2} }, - {"text/plain", {Fl::clipboard_plain_text, 1} }, - {"text/uri-list", {Fl::clipboard_plain_text, 2} }, - {"UTF8_STRING", {Fl::clipboard_plain_text, 3} }, - {wld_plain_text_clipboard, {Fl::clipboard_plain_text, 4} }, -}; - -// map: for each FLTK-clipboard-type, give current preferred mime-type and priority -typedef struct { const char *mime_type; int priority; } mime_prio_struct; -static std::map<const char * const, mime_prio_struct> clipboard_kinds_map { -// FLTK-clipboard-type current mime-type current highest priority - {Fl::clipboard_image, {NULL, 0} }, - {Fl::clipboard_plain_text, {NULL, 0} }, -}; - - -static void data_offer_handle_offer(void *data, struct wl_data_offer *offer, - const char *mime_type) { - // runs when app becomes active once for each offered clipboard type -//fprintf(stderr, "Clipboard offer=%p supports MIME type: %s\n", offer, mime_type); - std::map<const char*const, type_prio_struct, compare_utf8>::iterator iter_mime = - clipboard_mimetypes_map.find(mime_type); - if (iter_mime == clipboard_mimetypes_map.end()) return; // FLTK doesn't handle this mime_type - std::map<const char*const, mime_prio_struct>::iterator iter_kind = - clipboard_kinds_map.find(iter_mime->second.fltk_type); - if (iter_mime->second.priority > iter_kind->second.priority) { // found mime-type with higher priority - iter_kind->second.priority = iter_mime->second.priority; - iter_kind->second.mime_type = iter_mime->first; - fl_selection_type[1] = iter_kind->first; -//fprintf(stderr,"mime_type=%s priority=%d [%s]\n",iter_kind->second.mime_type, iter_kind->second.priority, fl_selection_type[1]); - } -} - - -static void data_offer_handle_source_actions(void *data, struct wl_data_offer *offer, - uint32_t actions) { - if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { - //printf("Drag supports the copy action\n"); - } -} - - -static void data_offer_handle_action(void *data, struct wl_data_offer *offer, - uint32_t dnd_action) { - switch (dnd_action) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: - //printf("A move action would be performed if dropped\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - //printf("A copy action would be performed if dropped\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: - //printf("The drag would be rejected if dropped\n"); - break; - } -} - - -static const struct wl_data_offer_listener data_offer_listener = { - .offer = data_offer_handle_offer, - .source_actions = data_offer_handle_source_actions, - .action = data_offer_handle_action, -}; - - -static void data_device_handle_data_offer(void *data, struct wl_data_device *data_device, - struct wl_data_offer *offer) { - // An application has created a new data source -//fprintf(stderr, "data_device_handle_data_offer offer=%p\n", offer); - fl_selection_type[1] = NULL; - wl_data_offer_add_listener(offer, &data_offer_listener, NULL); - // reset current best mime-type and priority - std::map<const char*const, mime_prio_struct>::iterator iter = clipboard_kinds_map.begin(); - while (iter != clipboard_kinds_map.end()) { - iter->second.mime_type = NULL; - iter->second.priority = 0; - iter++; - } -} - - -static void data_device_handle_selection(void *data, struct wl_data_device *data_device, - struct wl_data_offer *offer) { - // An application has set the clipboard contents. W -//fprintf(stderr, "data_device_handle_selection\n"); - if (fl_selection_offer) wl_data_offer_destroy(fl_selection_offer); - fl_selection_offer = offer; -//if (offer == NULL) fprintf(stderr, "Clipboard is empty\n"); -} - - -// Gets from the system the clipboard or dnd text and puts it in fl_selection_buffer[1] -// which is enlarged if necessary. -static void get_clipboard_or_dragged_text(struct wl_data_offer *offer) { - int fds[2]; - char *from; - if (pipe(fds)) return; - // preferred mime-type for the text clipboard type - const char *type = clipboard_kinds_map[Fl::clipboard_plain_text].mime_type; - wl_data_offer_receive(offer, type, fds[1]); - close(fds[1]); - wl_display_flush(Fl_Wayland_Screen_Driver::wl_display); - // read in fl_selection_buffer - char *to = fl_selection_buffer[1]; - ssize_t rest = fl_selection_buffer_length[1]; - while (rest) { - ssize_t n = read(fds[0], to, rest); - if (n <= 0) { - close(fds[0]); - fl_selection_length[1] = to - fl_selection_buffer[1]; - fl_selection_buffer[1][ fl_selection_length[1] ] = 0; - goto way_out; - } - n = Fl_Screen_Driver::convert_crlf(to, n); - to += n; - rest -= n; - } - // compute size of unread clipboard data - rest = fl_selection_buffer_length[1]; - while (true) { - char buf[1000]; - ssize_t n = read(fds[0], buf, sizeof(buf)); - if (n <= 0) { - close(fds[0]); - break; - } - rest += n; - } -//fprintf(stderr, "get_clipboard_or_dragged_text: size=%ld\n", rest); - // read full clipboard data - if (pipe(fds)) goto way_out; - wl_data_offer_receive(offer, type, fds[1]); - close(fds[1]); - wl_display_flush(Fl_Wayland_Screen_Driver::wl_display); - if (rest+1 > fl_selection_buffer_length[1]) { - delete[] fl_selection_buffer[1]; - fl_selection_buffer[1] = new char[rest+1000+1]; - fl_selection_buffer_length[1] = rest+1000; - } - from = fl_selection_buffer[1]; - while (rest > 0) { - ssize_t n = read(fds[0], from, rest); - if (n <= 0) break; - n = Fl_Screen_Driver::convert_crlf(from, n); - from += n; - rest -= n; - } - close(fds[0]); - fl_selection_length[1] = from - fl_selection_buffer[1]; - fl_selection_buffer[1][fl_selection_length[1]] = 0; -way_out: - if (strcmp(type, "text/uri-list") == 0) { - fl_decode_uri(fl_selection_buffer[1]); // decode encoded bytes - char *p = fl_selection_buffer[1]; - while (*p) { // remove prefixes - if (strncmp(p, "file://", 7) == 0) { - memmove(p, p+7, strlen(p+7)+1); - } - p = strchr(p, '\n'); - if (!p) break; - if (*++p == 0) *(p-1) = 0; // remove last '\n' - } - fl_selection_length[1] = strlen(fl_selection_buffer[1]); - } - Fl::e_clipboard_type = Fl::clipboard_plain_text; -} - - -static struct wl_data_offer *current_drag_offer = NULL; -static uint32_t fl_dnd_serial; - - -static void data_device_handle_enter(void *data, struct wl_data_device *data_device, - uint32_t serial, struct wl_surface *surface, - wl_fixed_t x, wl_fixed_t y, - struct wl_data_offer *offer) { - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); -//printf("Drag entered our surface %p(win=%p) at %dx%d\n", surface, win, wl_fixed_to_int(x), wl_fixed_to_int(y)); - if (win) { - fl_dnd_target_surface = surface; - float f = Fl::screen_scale(win->screen_num()); - Fl::e_x = wl_fixed_to_int(x) / f; - Fl::e_y = wl_fixed_to_int(y) / f; - while (win->parent()) { - Fl::e_x += win->x(); - Fl::e_y += win->y(); - win = win->window(); - } - fl_dnd_target_window = win; - Fl::e_x_root = Fl::e_x + fl_dnd_target_window->x(); - Fl::e_y_root = Fl::e_y + fl_dnd_target_window->y(); - Fl::handle(FL_DND_ENTER, fl_dnd_target_window); - current_drag_offer = offer; - fl_dnd_serial = serial; - } else fl_dnd_target_window = NULL; // we enter a non-FLTK window (titlebar, shade) - uint32_t supported_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - uint32_t preferred_action = supported_actions; - wl_data_offer_set_actions(offer, supported_actions, preferred_action); -} - - -static void data_device_handle_motion(void *data, struct wl_data_device *data_device, - uint32_t time, wl_fixed_t x, wl_fixed_t y) { - if (!current_drag_offer) return; -//printf("data_device_handle_motion fl_dnd_target_window=%p\n", fl_dnd_target_window); - int ret = 0; - if (fl_dnd_target_window) { - float f = Fl::screen_scale(fl_dnd_target_window->screen_num()); - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(fl_dnd_target_surface); - Fl::e_x = wl_fixed_to_int(x) / f; - Fl::e_y = wl_fixed_to_int(y) / f; - while (win->parent()) { - Fl::e_x += win->x(); - Fl::e_y += win->y(); - win = win->window(); - } - Fl::e_x_root = Fl::e_x + fl_dnd_target_window->x(); - Fl::e_y_root = Fl::e_y + fl_dnd_target_window->y(); - ret = Fl::handle(FL_DND_DRAG, fl_dnd_target_window); - if (Fl::belowmouse()) Fl::belowmouse()->take_focus(); - } - uint32_t supported_actions = ret && (Fl::pushed() || !doing_dnd) ? - WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY : WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - uint32_t preferred_action = supported_actions; - wl_data_offer_set_actions(current_drag_offer, supported_actions, preferred_action); - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - if (ret && current_drag_offer) wl_data_offer_accept(current_drag_offer, fl_dnd_serial, "text/plain"); -} - - -static void data_device_handle_leave(void *data, struct wl_data_device *data_device) { - //printf("Drag left our surface\n"); - if (current_drag_offer) Fl::handle(FL_DND_LEAVE, fl_dnd_target_window); -} - - -static void data_device_handle_drop(void *data, struct wl_data_device *data_device) { - if (!current_drag_offer) return; - Fl::handle(FL_ENTER, fl_dnd_target_window); // useful to set the belowmouse widget - int ret = Fl::handle(FL_DND_RELEASE, fl_dnd_target_window); -//printf("data_device_handle_drop ret=%d doing_dnd=%d\n", ret, doing_dnd); - - if (!ret) { - wl_data_offer_destroy(current_drag_offer); - current_drag_offer = NULL; - return; - } - - if (doing_dnd) { - Fl::e_text = fl_selection_buffer[0]; - Fl::e_length = fl_selection_length[0]; - } else { - get_clipboard_or_dragged_text(current_drag_offer); - Fl::e_text = fl_selection_buffer[1]; - Fl::e_length = fl_selection_length[1]; - } - int old_event = Fl::e_number; - Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); - Fl::e_number = old_event; - - wl_data_offer_finish(current_drag_offer); - wl_data_offer_destroy(current_drag_offer); - current_drag_offer = NULL; -} - - -static const struct wl_data_device_listener data_device_listener = { - .data_offer = data_device_handle_data_offer, - .enter = data_device_handle_enter, - .leave = data_device_handle_leave, - .motion = data_device_handle_motion, - .drop = data_device_handle_drop, - .selection = data_device_handle_selection, -}; - - -const struct wl_data_device_listener *Fl_Wayland_Screen_Driver::p_data_device_listener = - &data_device_listener; - - -// Reads from the clipboard an image which can be in image/bmp or image/png MIME type. -// Returns 0 if OK, != 0 if error. -static int get_clipboard_image(struct wl_data_offer *offer) { - int fds[2]; - if (pipe(fds)) return 1; - // preferred mime-type for the image clipboard type - const char *type = clipboard_kinds_map[Fl::clipboard_image].mime_type; - wl_data_offer_receive(offer, type, fds[1]); - close(fds[1]); - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - if (strcmp(type, "image/png") == 0) { - char tmp_fname[21]; - Fl_Shared_Image *shared = 0; - strcpy(tmp_fname, "/tmp/clipboardXXXXXX"); - int fd = mkstemp(tmp_fname); - if (fd >= 0) { - while (true) { - char buf[10000]; - ssize_t n = read(fds[0], buf, sizeof(buf)); - if (n <= 0) break; - n = write(fd, buf, n); - } - close(fd); - shared = Fl_Shared_Image::get(tmp_fname); - fl_unlink(tmp_fname); - } - close(fds[0]); - if (!shared) return 1; - int ld = shared->ld() ? shared->ld() : shared->w() * shared->d(); - uchar *rgb = new uchar[shared->w() * shared->h() * shared->d()]; - memcpy(rgb, shared->data()[0], ld * shared->h() ); - Fl_RGB_Image *image = new Fl_RGB_Image(rgb, shared->w(), shared->h(), shared->d(), - shared->ld()); - shared->release(); - image->alloc_array = 1; - Fl::e_clipboard_data = (void*)image; - } else { // process image/bmp - uchar buf[54]; - size_t rest = 1; - char *bmp = NULL; - ssize_t n = read(fds[0], buf, sizeof(buf)); // read size info of the BMP image - if (n == sizeof(buf)) { - int w, h; // size of the BMP image - Fl_Unix_System_Driver::read_int(buf + 18, w); - Fl_Unix_System_Driver::read_int(buf + 22, h); - // the number of bytes per row of BMP image, rounded up to multiple of 4 - int R = ((3*w+3)/4) * 4; - bmp = new char[R * h + 54]; - memcpy(bmp, buf, 54); - char *from = bmp + 54; - rest = R * h; - while (rest) { - ssize_t n = read(fds[0], from, rest); - if (n <= 0) break; - from += n; - rest -= n; - } -//fprintf(stderr, "get_clipboard_image: image/bmp %dx%d rest=%lu\n", w,h,rest); - } - close(fds[0]); - if (!rest) Fl::e_clipboard_data = Fl_Unix_System_Driver::own_bmp_to_RGB(bmp); - delete[] bmp; - if (rest) return 1; - } - Fl::e_clipboard_type = Fl::clipboard_image; - return 0; -} - - -void Fl_Wayland_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) { - if (clipboard != 1) return; - if (fl_i_own_selection[1]) { - // We already have it, do it quickly without compositor. - if (type == Fl::clipboard_plain_text && fl_selection_type[1] == type) { - Fl::e_text = fl_selection_buffer[1]; - Fl::e_length = fl_selection_length[1]; - if (!Fl::e_text) Fl::e_text = (char *)""; - } else if (type == Fl::clipboard_image && fl_selection_type[1] == type) { - Fl::e_clipboard_data = Fl_Unix_System_Driver::own_bmp_to_RGB(fl_selection_buffer[1]); - Fl::e_clipboard_type = Fl::clipboard_image; - } else return; - receiver.handle(FL_PASTE); - return; - } - // otherwise get the compositor to return it: - if (!fl_selection_offer) return; - if (type == Fl::clipboard_plain_text && clipboard_contains(Fl::clipboard_plain_text)) { - get_clipboard_or_dragged_text(fl_selection_offer); - Fl::e_text = fl_selection_buffer[1]; - Fl::e_length = fl_selection_length[1]; - receiver.handle(FL_PASTE); - } else if (type == Fl::clipboard_image && clipboard_contains(Fl::clipboard_image)) { - if (get_clipboard_image(fl_selection_offer)) return; - struct wld_window * xid = fl_wl_xid(receiver.top_window()); - if (xid) { - int s = Fl_Wayland_Window_Driver::driver(receiver.top_window())->wld_scale(); - if ( s > 1) { - Fl_RGB_Image *rgb = (Fl_RGB_Image*)Fl::e_clipboard_data; - rgb->scale(rgb->data_w() / s, rgb->data_h() / s); - } - } - int done = receiver.handle(FL_PASTE); - Fl::e_clipboard_type = ""; - if (done == 0) { - delete (Fl_RGB_Image*)Fl::e_clipboard_data; - Fl::e_clipboard_data = NULL; - } - } -} - - -void Fl_Wayland_Screen_Driver::copy(const char *stuff, int len, int clipboard, - const char *type) { - if (!stuff || len < 0) return; - - if (clipboard >= 2) - clipboard = 1; // Only on X11 do multiple clipboards make sense. - - if (len+1 > fl_selection_buffer_length[clipboard]) { - delete[] fl_selection_buffer[clipboard]; - fl_selection_buffer[clipboard] = new char[len+100]; - fl_selection_buffer_length[clipboard] = len+100; - } - memcpy(fl_selection_buffer[clipboard], stuff, len); - fl_selection_buffer[clipboard][len] = 0; // needed for direct paste - fl_selection_length[clipboard] = len; - fl_i_own_selection[clipboard] = 1; - fl_selection_type[clipboard] = Fl::clipboard_plain_text; - if (clipboard == 1) { - if (seat->data_source) wl_data_source_destroy(seat->data_source); - seat->data_source = wl_data_device_manager_create_data_source(seat->data_device_manager); - // we transmit the adequate value of index in fl_selection_buffer[index] - wl_data_source_add_listener(seat->data_source, &data_source_listener, (void*)1); - wl_data_source_offer(seat->data_source, wld_plain_text_clipboard); - wl_data_device_set_selection(seat->data_device, - seat->data_source, - seat->keyboard_enter_serial); -//fprintf(stderr, "wl_data_device_set_selection len=%d to %d\n", len, clipboard); - } -} - - -// takes a raw RGB image and puts it in the copy/paste buffer -void Fl_Wayland_Screen_Driver::copy_image(const unsigned char *data, int W, int H){ - if (!data || W <= 0 || H <= 0) return; - delete[] fl_selection_buffer[1]; - fl_selection_buffer[1] = - (char *)Fl_Unix_System_Driver::create_bmp(data,W,H,&fl_selection_length[1]); - fl_selection_buffer_length[1] = fl_selection_length[1]; - fl_i_own_selection[1] = 1; - fl_selection_type[1] = Fl::clipboard_image; - if (seat->data_source) wl_data_source_destroy(seat->data_source); - seat->data_source = wl_data_device_manager_create_data_source(seat->data_device_manager); - // we transmit the adequate value of index in fl_selection_buffer[index] - wl_data_source_add_listener(seat->data_source, &data_source_listener, (void*)1); - wl_data_source_offer(seat->data_source, "image/bmp"); - wl_data_device_set_selection(seat->data_device, seat->data_source, - seat->keyboard_enter_serial); -//fprintf(stderr, "copy_image: len=%d\n", fl_selection_length[1]); -} - -//////////////////////////////////////////////////////////////// -// Code for tracking clipboard changes: - -// is that possible with Wayland ? - -//////////////////////////////////////////////////////////////// - -#endif // !defined(FL_DOXYGEN) diff --git a/src/drivers/Wayland/fl_wayland_platform_init.cxx b/src/drivers/Wayland/fl_wayland_platform_init.cxx deleted file mode 100644 index 4c4477740..000000000 --- a/src/drivers/Wayland/fl_wayland_platform_init.cxx +++ /dev/null @@ -1,157 +0,0 @@ -// -// Wayland-specific code to initialize wayland support. -// -// Copyright 2022-2023 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 <FL/fl_config.h> -#include "Fl_Wayland_Copy_Surface_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "../Unix/Fl_Unix_System_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Image_Surface_Driver.H" -#include "../Base/Fl_Base_Pen_Events.H" -#ifdef FLTK_USE_X11 -# include "../Xlib/Fl_Xlib_Copy_Surface_Driver.H" -# include "../Cairo/Fl_X11_Cairo_Graphics_Driver.H" -# include "../X11/Fl_X11_Screen_Driver.H" -# include "../X11/Fl_X11_Window_Driver.H" -# include "../Xlib/Fl_Xlib_Image_Surface_Driver.H" -#endif -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - - -#ifdef FLTK_USE_X11 - -static bool attempt_wayland() { - if (Fl_Wayland_Screen_Driver::wl_display) return true; - static bool first = true; - static bool disable_wl = false; - if (first) { // get the value if it exists and cache it - void *sym = Fl_Posix_System_Driver::dlopen_or_dlsym(NULL, "fl_disable_wayland"); - if (sym) { - disable_wl = *(bool *)sym; - // printf("fl_disable_wayland = %s\n", disable_wl ? "true" : "false"); - } - first = false; - } - if (disable_wl) - return false; - const char *backend = ::getenv("FLTK_BACKEND"); - // fprintf(stderr, "FLTK_BACKEND='%s'\n", backend ? backend : ""); - if (backend && strcmp(backend, "x11") == 0) { - return false; - } - - if (backend && strcmp(backend, "wayland") == 0) { - Fl_Wayland_Screen_Driver::wl_display = wl_display_connect(NULL); - if (!Fl_Wayland_Screen_Driver::wl_display) { - fprintf(stderr, "Error: no Wayland connection available, FLTK_BACKEND='wayland'\n"); - exit(1); - } - return true; - } - - if (!backend) { - // env var XDG_RUNTIME_DIR is required for Wayland - const char *xdgrt = ::getenv("XDG_RUNTIME_DIR"); - if (xdgrt) { - // is a Wayland connection available ? - Fl_Wayland_Screen_Driver::wl_display = wl_display_connect(NULL); - if (Fl_Wayland_Screen_Driver::wl_display) { // Yes, use Wayland drivers - // puts("using wayland"); - return true; - } - } - // no Wayland connection or environment variable XDG_RUNTIME_DIR not set, - // falling back to X11 - return false; - } - - fprintf(stderr, "Error: unexpected value of FLTK_BACKEND: '%s'\n", backend); - exit(1); - return false; -} - -#endif // FLTK_USE_X11 - - -Fl_System_Driver *Fl_System_Driver::newSystemDriver() { - return new Fl_Unix_System_Driver(); -} - - -Fl_Graphics_Driver *Fl_Graphics_Driver::newMainGraphicsDriver() { -#ifdef FLTK_USE_X11 - if (!attempt_wayland()) return new Fl_X11_Cairo_Graphics_Driver(); -#endif - return new Fl_Wayland_Graphics_Driver(); -} - - -Fl_Copy_Surface_Driver *Fl_Copy_Surface_Driver::newCopySurfaceDriver(int w, int h) { -#ifdef FLTK_USE_X11 - if (!Fl_Wayland_Screen_Driver::wl_display) return new Fl_Xlib_Copy_Surface_Driver(w, h); -#endif - return new Fl_Wayland_Copy_Surface_Driver(w, h); -} - - -Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver() { - if (!Fl_Screen_Driver::system_driver) Fl::system_driver(); -#ifdef FLTK_USE_X11 - if (attempt_wayland()) { - return new Fl_Wayland_Screen_Driver(); - } - - Fl_X11_Screen_Driver *d = new Fl_X11_Screen_Driver(); - for (int i = 0; i < MAX_SCREENS; i++) d->screens[i].scale = 1; - d->current_xft_dpi = 0.; // means the value of the Xft.dpi resource is still unknown - return d; -#else - return new Fl_Wayland_Screen_Driver(); -#endif -} - - -Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w) -{ -#ifdef FLTK_USE_X11 - if (!attempt_wayland()) return new Fl_X11_Window_Driver(w); -#endif - return new Fl_Wayland_Window_Driver(w); -} - - -Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, int h, int high_res, Fl_Offscreen off) -{ -#ifdef FLTK_USE_X11 - if (!attempt_wayland()) - return new Fl_Xlib_Image_Surface_Driver(w, h, high_res, off); -#endif - return new Fl_Wayland_Image_Surface_Driver(w, h, high_res, off); -} - -#if defined(FLTK_HAVE_PEN_SUPPORT) - -namespace Fl { -namespace Pen { -Driver default_driver; -Driver& driver = default_driver; -} // namespace Pen -} // namespace Fl - -#endif // FLTK_HAVE_PEN_SUPPORT |
