summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt104
-rw-r--r--src/Fl_Menu.cxx21
-rw-r--r--src/Fl_Native_File_Chooser_GTK.cxx3
-rw-r--r--src/Fl_Native_File_Chooser_Kdialog.cxx3
-rw-r--r--src/Fl_Window_Driver.H6
-rw-r--r--src/Fl_Window_Driver.cxx12
-rw-r--r--src/Fl_x.cxx281
-rw-r--r--src/Makefile45
-rw-r--r--src/drivers/Unix/Fl_Unix_System_Driver.H57
-rw-r--r--src/drivers/Unix/Fl_Unix_System_Driver.cxx960
-rw-r--r--src/drivers/Wayland/Fl_Font.H33
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx74
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx401
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H158
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx1039
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx107
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Screen_Driver.H175
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx1428
-rw-r--r--src/drivers/Wayland/Fl_Wayland_System_Driver.H34
-rw-r--r--src/drivers/Wayland/Fl_Wayland_System_Driver.cxx98
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Window_Driver.H172
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx1582
-rw-r--r--src/drivers/Wayland/Fl_wayland.cxx672
-rw-r--r--src/drivers/X11/Fl_X11_System_Driver.H30
-rw-r--r--src/drivers/X11/Fl_X11_System_Driver.cxx554
25 files changed, 7186 insertions, 863 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 61e30c3a3..0d3e05be4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -195,10 +195,11 @@ if (FLTK_USE_X11)
# X11 (including APPLE with X11)
set (DRIVER_FILES
- drivers/Posix/Fl_Posix_System_Driver.cxx
drivers/Posix/Fl_Posix_Printer_Driver.cxx
drivers/X11/Fl_X11_Screen_Driver.cxx
drivers/X11/Fl_X11_Window_Driver.cxx
+ drivers/Posix/Fl_Posix_System_Driver.cxx
+ drivers/Unix/Fl_Unix_System_Driver.cxx
drivers/X11/Fl_X11_System_Driver.cxx
drivers/Xlib/Fl_Xlib_Graphics_Driver.cxx
drivers/Xlib/Fl_Xlib_Graphics_Driver_arci.cxx
@@ -241,6 +242,25 @@ if (FLTK_USE_X11)
drivers/Xlib/Fl_Font.H
)
+elseif (OPTION_USE_WAYLAND)
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_BINARY_DIR}")
+ set (DRIVER_FILES
+ drivers/Posix/Fl_Posix_System_Driver.cxx
+ drivers/Posix/Fl_Posix_Printer_Driver.cxx
+ drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
+ drivers/Wayland/Fl_Wayland_Window_Driver.cxx
+ drivers/Wayland/Fl_Wayland_System_Driver.cxx
+ drivers/Unix/Fl_Unix_System_Driver.cxx
+ drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx
+ drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx
+ drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx
+ drivers/Wayland/Fl_wayland.cxx
+ drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx
+ Fl_Native_File_Chooser_FLTK.cxx
+ Fl_Native_File_Chooser_GTK.cxx
+ Fl_Native_File_Chooser_Kdialog.cxx
+ )
+
elseif (APPLE)
# Apple Quartz
@@ -353,6 +373,8 @@ set (GL_DRIVER_FILES
)
if (FLTK_USE_X11)
set (GL_DRIVER_FILES ${GL_DRIVER_FILES} drivers/X11/Fl_X11_Gl_Window_Driver.cxx)
+elseif (OPTION_USE_WAYLAND)
+ set (GL_DRIVER_FILES ${GL_DRIVER_FILES} drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx)
elseif (APPLE)
set (GL_DRIVER_FILES ${GL_DRIVER_FILES} drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.cxx)
elseif (WIN32)
@@ -410,6 +432,40 @@ if (FLTK_USE_X11)
endif (NOT USE_XFT)
endif (FLTK_USE_X11)
+if (OPTION_USE_WAYLAND)
+ pkg_check_modules(DBUS dbus-1)
+ include_directories(${DBUS_INCLUDE_DIRS})
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_CURRENT_BINARY_DIR} -fPIC -D_GNU_SOURCE -DHAS_DBUS")
+ if (OPTION_USE_SYSTEM_LIBDECOR)
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_SYSTEM_LIBDECOR")
+ get_filename_component(PATH_TO_SHARED_LIBS ${HAVE_LIB_PANGO} DIRECTORY)
+ set (LIBDECOR_PLUGIN_DIR "\\\"${PATH_TO_SHARED_LIBS}/libdecor/plugins-1\\\" " )
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLIBDECOR_PLUGIN_DIR=${LIBDECOR_PLUGIN_DIR} ")
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_SYSTEM_LIBDECOR")
+ else()
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/../libdecor/src -DLIBDECOR_PLUGIN_API_VERSION=1 -DLIBDECOR_PLUGIN_DIR=\\\"/usr/local/lib\\\" ")
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_SYSTEM_LIBDECOR=0")
+ endif (OPTION_USE_SYSTEM_LIBDECOR)
+
+ if (GTK_FOUND)
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_GTK")
+ endif (GTK_FOUND)
+
+ list (APPEND CFILES
+ xutf8/keysym2Ucs.c
+ scandir_posix.c
+ ../libdecor/src/cursor-settings.c
+ ../libdecor/build/fl_libdecor-plugins.c
+ )
+ if (NOT OPTION_USE_SYSTEM_LIBDECOR)
+ list (APPEND CFILES
+ ../libdecor/build/fl_libdecor.c
+ ../libdecor/src/os-compatibility.c
+ ../libdecor/src/plugins/cairo/libdecor-cairo-blur.c
+ )
+ endif (NOT OPTION_USE_SYSTEM_LIBDECOR)
+endif (OPTION_USE_WAYLAND)
+
if (WIN32)
list (APPEND CFILES
scandir_win32.c
@@ -495,6 +551,52 @@ if (USE_XFT)
endif (LIB_fontconfig)
endif (USE_XFT)
+if (OPTION_USE_WAYLAND)
+ add_custom_command(
+ OUTPUT xdg-shell-protocol.c xdg-shell-client-protocol.h
+ COMMAND wayland-scanner private-code /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.c
+ COMMAND wayland-scanner client-header /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml xdg-shell-client-protocol.h
+ DEPENDS /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
+ VERBATIM
+ )
+ list (APPEND STATIC_FILES "xdg-shell-protocol.c")
+ list (APPEND SHARED_FILES "xdg-shell-protocol.c")
+ if (NOT OPTION_USE_SYSTEM_LIBDECOR)
+ add_custom_command(
+ OUTPUT xdg-decoration-protocol.c xdg-decoration-client-protocol.h
+ COMMAND wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml xdg-decoration-protocol.c
+ COMMAND wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml xdg-decoration-client-protocol.h
+ DEPENDS /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml
+ VERBATIM
+ )
+ list (APPEND STATIC_FILES "xdg-decoration-protocol.c")
+ list (APPEND SHARED_FILES "xdg-decoration-protocol.c")
+ endif (NOT OPTION_USE_SYSTEM_LIBDECOR)
+ add_custom_command(
+ OUTPUT text-input-protocol.c text-input-client-protocol.h
+ COMMAND wayland-scanner private-code /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml text-input-protocol.c
+ COMMAND wayland-scanner client-header /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml text-input-client-protocol.h
+ DEPENDS /usr/share/wayland-protocols/unstable/text-input/text-input-unstable-v3.xml
+ VERBATIM
+ )
+ list (APPEND STATIC_FILES "text-input-protocol.c")
+ list (APPEND SHARED_FILES "text-input-protocol.c")
+
+ if (OPTION_USE_GL)
+ list (APPEND OPTIONAL_LIBS "-lwayland-egl -lEGL")
+ endif (OPTION_USE_GL)
+ if (OPTION_USE_SYSTEM_LIBDECOR)
+ list (APPEND OPTIONAL_LIBS "-ldecor-0")
+ endif (OPTION_USE_SYSTEM_LIBDECOR)
+ list (APPEND OPTIONAL_LIBS "-lwayland-cursor -lwayland-client -lxkbcommon -ldl -ldbus-1")
+ if (GTK_FOUND)
+ list (APPEND OPTIONAL_LIBS ${GTK_LDFLAGS} )
+ endif (GTK_FOUND)
+ if (NOT OPTION_BUILD_SHARED_LIBS)
+ list (APPEND OPTIONAL_LIBS "-no-pie")
+ endif (NOT OPTION_BUILD_SHARED_LIBS)
+endif (OPTION_USE_WAYLAND)
+
#######################################################################
FL_ADD_LIBRARY (fltk STATIC "${STATIC_FILES}")
diff --git a/src/Fl_Menu.cxx b/src/Fl_Menu.cxx
index 62fbd56ba..7b153d38c 100644
--- a/src/Fl_Menu.cxx
+++ b/src/Fl_Menu.cxx
@@ -22,6 +22,7 @@
#include <FL/Fl.H>
#include "Fl_System_Driver.H"
+#include "Fl_Window_Driver.H"
#include <FL/Fl_Menu_Window.H>
#include <FL/Fl_Menu_.H>
#include <FL/fl_draw.H>
@@ -116,10 +117,13 @@ public:
// each vertical menu has one of these:
class menuwindow : public Fl_Menu_Window {
+ friend class Fl_Window_Driver;
+ friend class Fl_Menu_Item;
void draw();
void drawentry(const Fl_Menu_Item*, int i, int erase);
int handle_part1(int);
int handle_part2(int e, int ret);
+ static Fl_Window *parent_;
public:
menutitle* title;
int handle(int);
@@ -141,6 +145,12 @@ public:
int is_inside(int x, int y);
};
+Fl_Window *menuwindow::parent_ = NULL;
+
+Fl_Window *Fl_Window_Driver::menu_parent() {
+ return menuwindow::parent_;
+}
+
extern char fl_draw_shortcut;
/**
@@ -293,7 +303,7 @@ menuwindow::menuwindow(const Fl_Menu_Item* m, int X, int Y, int Wp, int Hp,
int scr_x, scr_y, scr_w, scr_h;
int tx = X, ty = Y;
- Fl::screen_work_area(scr_x, scr_y, scr_w, scr_h);
+ Fl_Window_Driver::driver(this)->menu_window_area(scr_x, scr_y, scr_w, scr_h);
if (!right_edge || right_edge > scr_x+scr_w) right_edge = scr_x+scr_w;
end();
@@ -439,14 +449,14 @@ void menuwindow::autoscroll(int n) {
int Y = y()+Fl::box_dx(box())+2+n*itemheight;
int xx, ww;
- Fl::screen_work_area(xx, scr_y, ww, scr_h);
+ Fl_Window_Driver::driver(this)->menu_window_area(xx, scr_y, ww, scr_h);
if (Y <= scr_y) Y = scr_y-Y+10;
else {
Y = Y+itemheight-scr_h-scr_y;
if (Y < 0) return;
Y = -Y-10;
}
- Fl_Menu_Window::position(x(), y()+Y);
+ Fl_Window_Driver::driver(this)->reposition_menu_window(x(), y()+Y);
// y(y()+Y); // don't wait for response from X
}
@@ -885,6 +895,7 @@ const Fl_Menu_Item* Fl_Menu_Item::pulldown(
button = pbutton;
if (pbutton && pbutton->window()) {
+ menuwindow::parent_ = pbutton->top_window();
for (Fl_Window* w = pbutton->window(); w; w = w->window()) {
X += w->x();
Y += w->y();
@@ -892,6 +903,7 @@ const Fl_Menu_Item* Fl_Menu_Item::pulldown(
} else {
X += Fl::event_x_root()-Fl::event_x();
Y += Fl::event_y_root()-Fl::event_y();
+ menuwindow::parent_ = Fl::first_window();
}
menuwindow mw(this, X, Y, W, H, initial_item, title, menubar);
Fl::grab(mw);
@@ -991,7 +1003,7 @@ const Fl_Menu_Item* Fl_Menu_Item::pulldown(
int dy = n->y()-nY;
int dx = n->x()-nX;
int waX, waY, waW, waH;
- Fl::screen_work_area(waX, waY, waW, waH, X, Y);
+ Fl_Window_Driver::driver(n)->menu_window_area(waX, waY, waW, waH, Fl::screen_num(X, Y));
for (int menu = 0; menu <= pp.menu_number; menu++) {
menuwindow* tt = pp.p[menu];
int nx = tt->x()+dx; if (nx < waX) {nx = waX; dx = -tt->x() + waX;}
@@ -1030,6 +1042,7 @@ const Fl_Menu_Item* Fl_Menu_Item::pulldown(
while (pp.nummenus>1) delete pp.p[--pp.nummenus];
mw.hide();
Fl::grab(0);
+ menuwindow::parent_ = NULL;
return m;
}
diff --git a/src/Fl_Native_File_Chooser_GTK.cxx b/src/Fl_Native_File_Chooser_GTK.cxx
index 07cc3ff7a..842115d37 100644
--- a/src/Fl_Native_File_Chooser_GTK.cxx
+++ b/src/Fl_Native_File_Chooser_GTK.cxx
@@ -772,7 +772,7 @@ int Fl_GTK_Native_File_Chooser_Driver::fl_gtk_chooser_wrapper()
Fl_Event_Dispatch old_dispatch = Fl::event_dispatch();
// prevent FLTK from processing any event
Fl::event_dispatch(fnfc_dispatch);
-
+ void *control = ((Fl_Unix_System_Driver*)Fl::system_driver())->control_maximize_button(NULL);
gint response_id = GTK_RESPONSE_NONE;
fl_g_signal_connect_data(gtkw_ptr, "response", G_CALLBACK(run_response_handler), &response_id, NULL, (GConnectFlags) 0);
while (response_id == GTK_RESPONSE_NONE) { // loop that shows the GTK dialog window
@@ -836,6 +836,7 @@ int Fl_GTK_Native_File_Chooser_Driver::fl_gtk_chooser_wrapper()
while (fl_gtk_events_pending ()) fl_gtk_main_iteration ();
Fl::event_dispatch(old_dispatch);
+ if (control) ((Fl_Unix_System_Driver*)Fl::system_driver())->control_maximize_button(control);
return result;
} // fl_gtk_chooser_wrapper
diff --git a/src/Fl_Native_File_Chooser_Kdialog.cxx b/src/Fl_Native_File_Chooser_Kdialog.cxx
index 814f19d97..5955659d6 100644
--- a/src/Fl_Native_File_Chooser_Kdialog.cxx
+++ b/src/Fl_Native_File_Chooser_Kdialog.cxx
@@ -18,6 +18,7 @@
#include <FL/Fl_Native_File_Chooser.H>
#include "Fl_Native_File_Chooser_Kdialog.H"
#include "Fl_Window_Driver.H"
+#include "drivers/Unix/Fl_Unix_System_Driver.H"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -129,12 +130,14 @@ int Fl_Kdialog_Native_File_Chooser_Driver::show() {
Fl_Event_Dispatch old_dispatch = Fl::event_dispatch();
// prevent FLTK from processing any event
Fl::event_dispatch(fnfc_dispatch);
+ void *control = ((Fl_Unix_System_Driver*)Fl::system_driver())->control_maximize_button(NULL);
// run event loop until pipe finishes
while (data.fd >= 0) Fl::wait();
Fl::remove_fd(fileno(pipe));
pclose(pipe);
// return to previous event processing by FLTK
Fl::event_dispatch(old_dispatch);
+ if (control) ((Fl_Unix_System_Driver*)Fl::system_driver())->control_maximize_button(control);
if (data.all_files) {
// process text received from pipe
if (data.all_files[strlen(data.all_files)-1] == '\n') data.all_files[strlen(data.all_files)-1] = 0;
diff --git a/src/Fl_Window_Driver.H b/src/Fl_Window_Driver.H
index 85f758bfb..3766980a3 100644
--- a/src/Fl_Window_Driver.H
+++ b/src/Fl_Window_Driver.H
@@ -184,6 +184,12 @@ public:
int /*dest_x*/, int /*dest_y*/,
void (*)(void*, int,int,int,int), void*) { return 0; }
static inline Fl_Window_Driver* driver(const Fl_Window *win) {return win->pWindowDriver;}
+
+ // --- support for menu windows
+ // the default implementation of next 2 members is most probably enough
+ virtual void reposition_menu_window(int x, int y);
+ virtual void menu_window_area(int &X, int &Y, int &W, int &H, int nscreen = -1);
+ static Fl_Window *menu_parent();
};
#endif // FL_WINDOW_DRIVER_H
diff --git a/src/Fl_Window_Driver.cxx b/src/Fl_Window_Driver.cxx
index a21776381..025aa36ea 100644
--- a/src/Fl_Window_Driver.cxx
+++ b/src/Fl_Window_Driver.cxx
@@ -26,6 +26,7 @@
#include <FL/fl_draw.H>
#include <FL/Fl.H>
#include <FL/platform.H>
+#include "Fl_Screen_Driver.H"
extern void fl_throw_focus(Fl_Widget *o);
@@ -247,6 +248,17 @@ void Fl_Window_Driver::resize_after_scale_change(int ns, float old_f, float new_
is_a_rescale_ = false;
}
+void Fl_Window_Driver::reposition_menu_window(int x, int y) {
+ if (y != pWindow->y() || x != pWindow->x()) pWindow->Fl_Widget::position(x, y);
+}
+
+void Fl_Window_Driver::menu_window_area(int &X, int &Y, int &W, int &H, int nscreen) {
+ int mx, my;
+ Fl_Screen_Driver *scr_driver = Fl::screen_driver();
+ if (nscreen < 0) nscreen = scr_driver->get_mouse(mx, my);
+ scr_driver->screen_work_area(X, Y, W, H, nscreen);
+}
+
/**
\}
\endcond
diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx
index 34528a0b2..40c0e02ea 100644
--- a/src/Fl_x.cxx
+++ b/src/Fl_x.cxx
@@ -78,155 +78,16 @@ static bool have_xfixes = false;
# include <X11/extensions/Xrender.h>
# endif
-extern Fl_Widget *fl_selection_requestor;
-
-static void open_display_i(Display *d); // open display (internal)
-
-////////////////////////////////////////////////////////////////
-
-// Hack to speed up bg box drawing - aka "boxcheat":
-// If the boxtype of a window is a filled rectangle, we can make the
-// redisplay *look* faster by using X's background pixel erasing.
-// This is done by setting a flag when the window is shown for the first time.
-
-// Note to FLTK devs:
-// This can cause unexpected behavior, for instance if the box() or
-// color() of a window is changed after show(), and it does presumably not
-// have much effect on current systems (compared to 1998).
-// It is also fragile WRT checking the box type if any other scheme than
-// the default scheme is loaded.
-// Hence this is disabled since FLTK 1.4.0 (AlbrechtS Feb 02, 2022)
-
-// Note to FLTK users:
-// You may define ENABLE_BOXCHEAT to use it anyway but please tell the
-// FLTK devs why you believe that you need it. Should we re-enable it?
-
-#ifdef ENABLE_BOXCHEAT
-static int fl_background_pixel = -1;
-static inline int can_boxcheat(Fl_Boxtype b) {
- return (b == 1 || ((b & 2) && b <= 15));
-}
-#endif // (ENABLE_BOXCHEAT)
-
-////////////////////////////////////////////////////////////////
-// interface to poll/select call:
-
# if USE_POLL
-
# include <poll.h>
-static pollfd *pollfds = 0;
-
# else
-# if HAVE_SYS_SELECT_H
-# include <sys/select.h>
-# endif /* HAVE_SYS_SELECT_H */
-
-// The following #define is only needed for HP-UX 9.x and earlier:
-//#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
-
-static fd_set fdsets[3];
-static int maxfd;
# define POLLIN 1
-# define POLLOUT 4
-# define POLLERR 8
-
# endif /* USE_POLL */
-static int nfds = 0;
-static int fd_array_size = 0;
-struct FD {
-# if !USE_POLL
- int fd;
- short events;
-# endif
- void (*cb)(int, void*);
- void* arg;
-};
-
-static FD *fd = 0;
-
-void Fl_X11_System_Driver::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
- remove_fd(n,events);
- int i = nfds++;
- if (i >= fd_array_size) {
- FD *temp;
- fd_array_size = 2*fd_array_size+1;
-
- if (!fd) temp = (FD*)malloc(fd_array_size*sizeof(FD));
- else temp = (FD*)realloc(fd, fd_array_size*sizeof(FD));
-
- if (!temp) return;
- fd = temp;
-
-# if USE_POLL
- pollfd *tpoll;
-
- if (!pollfds) tpoll = (pollfd*)malloc(fd_array_size*sizeof(pollfd));
- else tpoll = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
-
- if (!tpoll) return;
- pollfds = tpoll;
-# endif
- }
- fd[i].cb = cb;
- fd[i].arg = v;
-# if USE_POLL
- pollfds[i].fd = n;
- pollfds[i].events = events;
-# else
- fd[i].fd = n;
- fd[i].events = events;
- if (events & POLLIN) FD_SET(n, &fdsets[0]);
- if (events & POLLOUT) FD_SET(n, &fdsets[1]);
- if (events & POLLERR) FD_SET(n, &fdsets[2]);
- if (n > maxfd) maxfd = n;
-# endif
-}
-
-void Fl_X11_System_Driver::add_fd(int n, void (*cb)(int, void*), void* v) {
- add_fd(n, POLLIN, cb, v);
-}
+extern Fl_Widget *fl_selection_requestor;
-void Fl_X11_System_Driver::remove_fd(int n, int events) {
- int i,j;
-# if !USE_POLL
- maxfd = -1; // recalculate maxfd on the fly
-# endif
- for (i=j=0; i<nfds; i++) {
-# if USE_POLL
- if (pollfds[i].fd == n) {
- int e = pollfds[i].events & ~events;
- if (!e) continue; // if no events left, delete this fd
- pollfds[j].events = e;
- }
-# else
- if (fd[i].fd == n) {
- int e = fd[i].events & ~events;
- if (!e) continue; // if no events left, delete this fd
- fd[i].events = e;
- }
- if (fd[i].fd > maxfd) maxfd = fd[i].fd;
-# endif
- // move it down in the array if necessary:
- if (j<i) {
- fd[j] = fd[i];
-# if USE_POLL
- pollfds[j] = pollfds[i];
-# endif
- }
- j++;
- }
- nfds = j;
-# if !USE_POLL
- if (events & POLLIN) FD_CLR(n, &fdsets[0]);
- if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
- if (events & POLLERR) FD_CLR(n, &fdsets[2]);
-# endif
-}
+static void open_display_i(Display *d); // open display (internal)
-void Fl_X11_System_Driver::remove_fd(int n) {
- remove_fd(n, -1);
-}
extern int fl_send_system_handlers(void *e);
@@ -255,10 +116,6 @@ static void do_queued_events() {
#endif
}
-// these pointers are set by the Fl::lock() function:
-static void nothing() {}
-void (*fl_lock_function)() = nothing;
-void (*fl_unlock_function)() = nothing;
// This is never called with time_to_wait < 0.0:
// It should return negative on error, 0 if nothing happens before
@@ -269,69 +126,13 @@ int Fl_X11_System_Driver::poll_or_select_with_delay(double time_to_wait) {
// unnecessarily and thus cause the file descriptor to not be ready,
// so we must check for already-read events:
if (fl_display && XQLength(fl_display)) {do_queued_events(); return 1;}
-
-# if !USE_POLL
- fd_set fdt[3];
- fdt[0] = fdsets[0];
- fdt[1] = fdsets[1];
- fdt[2] = fdsets[2];
-# endif
- int n;
-
- fl_unlock_function();
-
- if (time_to_wait < 2147483.648) {
-# if USE_POLL
- n = ::poll(pollfds, nfds, int(time_to_wait*1000 + .5));
-# else
- timeval t;
- t.tv_sec = int(time_to_wait);
- t.tv_usec = int(1000000 * (time_to_wait-t.tv_sec));
- n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
-# endif
- } else {
-# if USE_POLL
- n = ::poll(pollfds, nfds, -1);
-# else
- n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
-# endif
- }
-
- fl_lock_function();
-
- if (n > 0) {
- for (int i=0; i<nfds; i++) {
-# if USE_POLL
- if (pollfds[i].revents) fd[i].cb(pollfds[i].fd, fd[i].arg);
-# else
- int f = fd[i].fd;
- short revents = 0;
- if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
- if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
- if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
- if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
-# endif
- }
- }
- return n;
+ return Fl_Unix_System_Driver::poll_or_select_with_delay(time_to_wait);
}
// just like Fl_X11_System_Driver::poll_or_select_with_delay(0.0) except no callbacks are done:
int Fl_X11_System_Driver::poll_or_select() {
if (XQLength(fl_display)) return 1;
- if (!nfds) return 0; // nothing to select or poll
-# if USE_POLL
- return ::poll(pollfds, nfds, 0);
-# else
- timeval t;
- t.tv_sec = 0;
- t.tv_usec = 0;
- fd_set fdt[3];
- fdt[0] = fdsets[0];
- fdt[1] = fdsets[1];
- fdt[2] = fdsets[2];
- return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
-# endif
+ return Fl_Unix_System_Driver::poll_or_select();
}
// replace \r\n by \n
@@ -883,28 +684,6 @@ static void read_int(uchar *c, int& i) {
i |= (*(++c))<<24;
}
-// turn BMP image FLTK produced by create_bmp() back to Fl_RGB_Image
-static Fl_RGB_Image *own_bmp_to_RGB(char *bmp) {
- int w, h;
- read_int((uchar*)bmp + 18, w);
- read_int((uchar*)bmp + 22, h);
- int R=(3*w+3)/4 * 4; // the number of bytes per row, rounded up to multiple of 4
- bmp += 54;
- uchar *data = new uchar[w*h*3];
- uchar *p = data;
- for (int i = h-1; i >= 0; i--) {
- char *s = bmp + i * R;
- for (int j = 0; j < w; j++) {
- *p++=s[2];
- *p++=s[1];
- *p++=s[0];
- s+=3;
- }
- }
- Fl_RGB_Image *img = new Fl_RGB_Image(data, w, h, 3);
- img->alloc_array = 1;
- return img;
-}
// Call this when a "paste" operation happens:
void Fl_X11_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) {
@@ -918,7 +697,7 @@ void Fl_X11_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char
Fl::e_length = fl_selection_length[clipboard];
if (!Fl::e_text) Fl::e_text = (char *)"";
} else if (clipboard == 1 && type == Fl::clipboard_image && fl_selection_type[1] == type) {
- Fl::e_clipboard_data = own_bmp_to_RGB(fl_selection_buffer[1]);
+ 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;
int retval = receiver.handle(FL_PASTE);
@@ -1074,53 +853,12 @@ static void write_int(unsigned char **cp, int i) {
*cp = c;
}
-static unsigned char *create_bmp(const unsigned char *data, int W, int H, int *return_size){
- int R=(3*W+3)/4 * 4; // the number of bytes per row, rounded up to multiple of 4
- int s=H*R;
- int fs=14+40+s;
- unsigned char *b=new unsigned char[fs];
- unsigned char *c=b;
- // BMP header
- *c++='B';
- *c++='M';
- write_int(&c,fs);
- write_int(&c,0);
- write_int(&c,14+40);
- // DIB header:
- write_int(&c,40);
- write_int(&c,W);
- write_int(&c,H);
- write_short(&c,1);
- write_short(&c,24);//bits ber pixel
- write_int(&c,0);//RGB
- write_int(&c,s);
- write_int(&c,0);// horizontal resolution
- write_int(&c,0);// vertical resolution
- write_int(&c,0);//number of colors. 0 -> 1<<bits_per_pixel
- write_int(&c,0);
- // Pixel data
- data+=3*W*H;
- for (int y=0;y<H;++y){
- data-=3*W;
- const unsigned char *s=data;
- unsigned char *p=c;
- for (int x=0;x<W;++x){
- *p++=s[2];
- *p++=s[1];
- *p++=s[0];
- s+=3;
- }
- c+=R;
- }
- *return_size = fs;
- return b;
-}
// takes a raw RGB image and puts it in the copy/paste buffer
void Fl_X11_Screen_Driver::copy_image(const unsigned char *data, int W, int H, int clipboard){
if (!data || W <= 0 || H <= 0) return;
delete[] fl_selection_buffer[clipboard];
- fl_selection_buffer[clipboard] = (char *) create_bmp(data,W,H,&fl_selection_length[clipboard]);
+ fl_selection_buffer[clipboard] = (char *) Fl_Unix_System_Driver::create_bmp(data,W,H,&fl_selection_length[clipboard]);
fl_selection_buffer_length[clipboard] = fl_selection_length[clipboard];
fl_i_own_selection[clipboard] = 1;
fl_selection_type[clipboard] = Fl::clipboard_image;
@@ -3182,13 +2920,6 @@ int Fl_X11_Window_Driver::set_cursor(const Fl_RGB_Image *image, int hotx, int ho
////////////////////////////////////////////////////////////////
-// returns pointer to the filename, or null if name ends with '/'
-const char *Fl_X11_System_Driver::filename_name(const char *name) {
- const char *p,*q;
- if (!name) return (0);
- for (p=q=name; *p;) if (*p++ == '/') q = p;
- return q;
-}
void Fl_X11_Window_Driver::label(const char *name, const char *iname) {
if (shown() && !parent()) {
diff --git a/src/Makefile b/src/Makefile
index f98ca0279..457aa4b59 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -206,6 +206,8 @@ GLCPPFILES_X11 = drivers/X11/Fl_X11_Gl_Window_Driver.cxx
GLCPPFILES_XFT = $(GLCPPFILES_X11)
GLCPPFILES_WIN = drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.cxx
+GLCPPFILES_WAYLAND = drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx
+
GLCPPFILES += $(GLCPPFILES_$(BUILD))
# the following file currently doesn't contribute code to GLCPPFILES
@@ -263,6 +265,7 @@ XLIBCPPFILES = \
drivers/X11/Fl_X11_Window_Driver.cxx \
drivers/X11/Fl_X11_Screen_Driver.cxx \
drivers/Posix/Fl_Posix_System_Driver.cxx \
+ drivers/Unix/Fl_Unix_System_Driver.cxx \
drivers/X11/Fl_X11_System_Driver.cxx \
drivers/Posix/Fl_Posix_Printer_Driver.cxx \
Fl_x.cxx \
@@ -271,6 +274,25 @@ XLIBCPPFILES = \
Fl_Native_File_Chooser_GTK.cxx\
Fl_Native_File_Chooser_Kdialog.cxx \
Fl_get_key.cxx
+
+# These C++ files are used under condition: BUILD_WAYLAND
+WLCPPFILES = \
+ drivers/Posix/Fl_Posix_Printer_Driver.cxx \
+ Fl_Native_File_Chooser_FLTK.cxx \
+ Fl_Native_File_Chooser_GTK.cxx \
+ Fl_Native_File_Chooser_Kdialog.cxx \
+ drivers/Posix/Fl_Posix_System_Driver.cxx \
+ drivers/Unix/Fl_Unix_System_Driver.cxx \
+ drivers/Wayland/Fl_Wayland_System_Driver.cxx \
+ drivers/Wayland/Fl_Wayland_Screen_Driver.cxx \
+ drivers/Wayland/Fl_Wayland_Window_Driver.cxx \
+ drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx \
+ drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx \
+ drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx \
+ drivers/Wayland/Fl_wayland.cxx
+
+
+# fl_dnd_x.cxx Fl_Native_File_Chooser_GTK.cxx
# This C file is used under condition: BUILD_X11
XLIBCFILES = \
@@ -290,6 +312,15 @@ XLIBFONTFILES = \
XLIBXFTFILES = \
drivers/Xlib/Fl_Xlib_Graphics_Driver_font_xft.cxx \
drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx
+
+# This C file is used under condition: BUILD_WAYLAND
+WLCFILES = \
+ xutf8/keysym2Ucs.c \
+ scandir_posix.c
+
+# These C++ files are used under condition: BUILD_WAYLAND
+WLXFTFILES = \
+ drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx
# These C++ files are used under condition: BUILD_GDI
GDICPPFILES = \
@@ -325,7 +356,7 @@ FLTKFLAGS = -DFL_LIBRARY
include ../makeinclude
# makeinclude has set this variable:
-# BUILD = {WIN|X11|XFT|OSX}
+# BUILD = {WIN|X11|XFT|OSX|WAYLAND}
MMFILES_OSX = $(OBJCPPFILES)
MMFILES = $(MMFILES_$(BUILD))
@@ -336,6 +367,8 @@ CPPFILES_OSX = $(QUARTZCPPFILES)
CPPFILES_XFT = $(XLIBCPPFILES) $(XLIBXFTFILES)
CPPFILES_X11 = $(XLIBCPPFILES) $(XLIBFONTFILES)
+CPPFILES_WAYLAND = $(WLCPPFILES) $(WLXFTFILES)
+
CPPFILES_WIN = $(GDICPPFILES)
CPPFILES += $(CPPFILES_$(BUILD))
@@ -344,12 +377,21 @@ CPPFILES += $(CPPFILES_$(BUILD))
CFILES_X11 = $(XLIBCFILES) $(XLIBXCFILES)
CFILES_XFT = $(XLIBCFILES)
+CFILES_WAYLAND = $(WLCFILES)
+EXTRA_OBJECTS_WAYLAND = ../libdecor/build/fl_libdecor.o ../libdecor/build/libdecor-cairo-blur.o \
+ ../libdecor/build/fl_libdecor-plugins.o \
+ xdg-decoration-protocol.o xdg-shell-protocol.o text-input-protocol.o \
+ ../libdecor/build/cursor-settings.o ../libdecor/build/os-compatibility.o
+EXTRA_CXXFLAGS_WAYLAND = -I.
+
CFILES_WIN = $(GDICFILES)
CFILES += $(CFILES_$(BUILD))
+CXXFLAGS += $(EXTRA_CXXFLAGS_$(BUILD))
OBJECTS = $(MMFILES:.mm=.o) $(CPPFILES:.cxx=.o) $(CFILES:.c=.o) $(UTF8CFILES:.c=.o)
+OBJECTS += $(EXTRA_OBJECTS_$(BUILD))
GLOBJECTS = $(GLCPPFILES:.cxx=.o)
FLOBJECTS = $(FLCPPFILES:.cxx=.o)
IMGOBJECTS = $(IMGCPPFILES:.cxx=.o)
@@ -619,6 +661,7 @@ clean:
-$(RM) drivers/WinAPI/*.o
-$(RM) drivers/X11/*.o
-$(RM) drivers/Xlib/*.o
+ -$(RM) drivers/Wayland/*.o
-$(RM) $(DSONAME) $(FLDSONAME) $(GLDSONAME) $(IMGDSONAME) \
$(LIBNAME) $(FLLIBNAME) $(GLLIBNAME) \
$(IMGLIBNAME) \
diff --git a/src/drivers/Unix/Fl_Unix_System_Driver.H b/src/drivers/Unix/Fl_Unix_System_Driver.H
new file mode 100644
index 000000000..9b1268b57
--- /dev/null
+++ b/src/drivers/Unix/Fl_Unix_System_Driver.H
@@ -0,0 +1,57 @@
+//
+// Definition of Wayland system driver
+// for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2021 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_NIX_SYSTEM_DRIVER_H
+#define FL_NIX_SYSTEM_DRIVER_H
+
+#include "../Posix/Fl_Posix_System_Driver.H"
+class Fl_RGB_Image;
+
+class FL_EXPORT Fl_Unix_System_Driver : public Fl_Posix_System_Driver {
+public:
+ Fl_Unix_System_Driver() : Fl_Posix_System_Driver() {
+ }
+ virtual int clocale_snprintf(char *output, size_t output_size, const char *format, va_list args);
+ virtual int clocale_sscanf(const char *input, const char *format, va_list args);
+ virtual int clocale_printf(FILE *output, const char *format, va_list args);
+ virtual int filename_list(const char *d, dirent ***list,
+ int (*sort)(struct dirent **, struct dirent **),
+ char *errmsg=NULL, int errmsg_sz=0);
+ virtual int open_uri(const char *uri, char *msg, int msglen);
+ virtual int use_tooltip_timeout_condition() {return 1;}
+ virtual int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon);
+ virtual void newUUID(char *uuidBuffer);
+ virtual char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
+ const char *application);
+ virtual int preferences_need_protection_check() {return 1;}
+ virtual int utf8locale();
+ virtual const char *filename_name(const char *buf);
+ virtual void add_fd(int fd, int when, Fl_FD_Handler cb, void* = 0);
+ virtual void add_fd(int fd, Fl_FD_Handler cb, void* = 0);
+ virtual void remove_fd(int, int when);
+ virtual void remove_fd(int);
+ double wait(double time_to_wait);
+ int ready();
+ // 3 additional virtual members
+ virtual int poll_or_select_with_delay(double time_to_wait);
+ virtual int poll_or_select();
+ virtual void *control_maximize_button(void *data);
+ static unsigned char *create_bmp(const unsigned char *data, int W, int H, int *return_size);
+ static Fl_RGB_Image *own_bmp_to_RGB(char *bmp);
+};
+
+#endif /* FL_NIX_SYSTEM_DRIVER_H */
diff --git a/src/drivers/Unix/Fl_Unix_System_Driver.cxx b/src/drivers/Unix/Fl_Unix_System_Driver.cxx
new file mode 100644
index 000000000..b564df5a5
--- /dev/null
+++ b/src/drivers/Unix/Fl_Unix_System_Driver.cxx
@@ -0,0 +1,960 @@
+//
+// Definition of Unix/Linux system driver
+// for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2010-2021 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_Unix_System_Driver.H"
+#include <FL/Fl_File_Browser.H>
+#include <FL/fl_string_functions.h> // fl_strdup
+#include <FL/platform.H>
+#include "../../flstring.h"
+#include "../../Fl_Timeout.h"
+
+#include <locale.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <string.h> // strerror(errno)
+#include <errno.h> // errno
+#if HAVE_DLSYM && HAVE_DLFCN_H
+#include <dlfcn.h> // for dlsym
+#endif
+
+
+#if defined(_AIX)
+extern "C" {
+# include <sys/vmount.h>
+# include <sys/mntctl.h>
+ // Older AIX versions don't expose this prototype
+ int mntctl(int, int, char *);
+}
+#endif // _AIX
+
+#if defined(__NetBSD__)
+extern "C" {
+# include <sys/param.h> // For '__NetBSD_Version__' definition
+# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 300000000)
+# include <sys/types.h>
+# include <sys/statvfs.h>
+# if defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_H)
+# include <pthread.h>
+# endif // HAVE_PTHREAD && HAVE_PTHREAD_H
+# ifdef HAVE_PTHREAD
+ static pthread_mutex_t getvfsstat_mutex = PTHREAD_MUTEX_INITIALIZER;
+# endif // HAVE_PTHREAD/
+# endif // __NetBSD_Version__
+}
+#endif // __NetBSD__
+
+#ifndef HAVE_SCANDIR
+extern "C" {
+ int fl_scandir(const char *dirname, struct dirent ***namelist,
+ int (*select)(struct dirent *),
+ int (*compar)(struct dirent **, struct dirent **),
+ char *errmsg, int errmsg_sz);
+}
+#endif
+
+#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
+static locale_t c_locale = NULL;
+#endif
+
+
+int Fl_Unix_System_Driver::clocale_printf(FILE *output, const char *format, va_list args) {
+#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
+ if (!c_locale)
+ c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
+ locale_t previous_locale = uselocale(c_locale);
+ int retval = vfprintf(output, format, args);
+ uselocale(previous_locale);
+#else
+ char *saved_locale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ int retval = vfprintf(output, format, args);
+ setlocale(LC_NUMERIC, saved_locale);
+#endif
+ return retval;
+}
+
+int Fl_Unix_System_Driver::clocale_snprintf(char *output, size_t output_size, const char *format, va_list args) {
+#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
+ if (!c_locale)
+ c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
+ locale_t previous_locale = uselocale(c_locale);
+ int retval = vsnprintf(output, output_size, format, args);
+ uselocale(previous_locale);
+#else
+ char *saved_locale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ int retval = vsnprintf(output, output_size, format, args);
+ setlocale(LC_NUMERIC, saved_locale);
+#endif
+ return retval;
+}
+
+int Fl_Unix_System_Driver::clocale_sscanf(const char *input, const char *format, va_list args) {
+#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
+ if (!c_locale)
+ c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
+ locale_t previous_locale = uselocale(c_locale);
+ int retval = vsscanf(input, format, args);
+ uselocale(previous_locale);
+#else
+ char *saved_locale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ int retval = vsscanf(input, format, args);
+ setlocale(LC_NUMERIC, saved_locale);
+#endif
+ return retval;
+}
+
+
+// Find a program in the path...
+static char *path_find(const char *program, char *filename, int filesize) {
+ const char *path; // Search path
+ char *ptr, // Pointer into filename
+ *end; // End of filename buffer
+
+
+ if ((path = fl_getenv("PATH")) == NULL) path = "/bin:/usr/bin";
+
+ for (ptr = filename, end = filename + filesize - 1; *path; path ++) {
+ if (*path == ':') {
+ if (ptr > filename && ptr[-1] != '/' && ptr < end) *ptr++ = '/';
+
+ strlcpy(ptr, program, end - ptr + 1);
+
+ if (!access(filename, X_OK)) return filename;
+
+ ptr = filename;
+ } else if (ptr < end) *ptr++ = *path;
+ }
+
+ if (ptr > filename) {
+ if (ptr[-1] != '/' && ptr < end) *ptr++ = '/';
+
+ strlcpy(ptr, program, end - ptr + 1);
+
+ if (!access(filename, X_OK)) return filename;
+ }
+
+ return 0;
+}
+
+
+int Fl_Unix_System_Driver::open_uri(const char *uri, char *msg, int msglen)
+{
+ // Run any of several well-known commands to open the URI.
+ //
+ // We give preference to the Portland group's xdg-utils
+ // programs which run the user's preferred web browser, etc.
+ // based on the current desktop environment in use. We fall
+ // back on older standards and then finally test popular programs
+ // until we find one we can use.
+ //
+ // Note that we specifically do not support the MAILER and
+ // BROWSER environment variables because we have no idea whether
+ // we need to run the listed commands in a terminal program.
+ char command[FL_PATH_MAX], // Command to run...
+ *argv[4], // Command-line arguments
+ remote[1024]; // Remote-mode command...
+ const char * const *commands; // Array of commands to check...
+ int i;
+ static const char * const browsers[] = {
+ "xdg-open", // Portland
+ "htmlview", // Freedesktop.org
+ "firefox",
+ "mozilla",
+ "netscape",
+ "konqueror", // KDE
+ "opera",
+ "hotjava", // Solaris
+ "mosaic",
+ NULL
+ };
+ static const char * const readers[] = {
+ "xdg-email", // Portland
+ "thunderbird",
+ "mozilla",
+ "netscape",
+ "evolution", // GNOME
+ "kmailservice", // KDE
+ NULL
+ };
+ static const char * const managers[] = {
+ "xdg-open", // Portland
+ "fm", // IRIX
+ "dtaction", // CDE
+ "nautilus", // GNOME
+ "konqueror", // KDE
+ NULL
+ };
+
+ // Figure out which commands to check for...
+ if (!strncmp(uri, "file://", 7)) commands = managers;
+ else if (!strncmp(uri, "mailto:", 7) ||
+ !strncmp(uri, "news:", 5)) commands = readers;
+ else commands = browsers;
+
+ // Find the command to run...
+ for (i = 0; commands[i]; i ++)
+ if (path_find(commands[i], command, sizeof(command))) break;
+
+ if (!commands[i]) {
+ if (msg) {
+ snprintf(msg, msglen, "No helper application found for \"%s\"", uri);
+ }
+
+ return 0;
+ }
+
+ // Handle command-specific arguments...
+ argv[0] = (char *)commands[i];
+
+ if (!strcmp(commands[i], "firefox") ||
+ !strcmp(commands[i], "mozilla") ||
+ !strcmp(commands[i], "netscape") ||
+ !strcmp(commands[i], "thunderbird")) {
+ // program -remote openURL(uri)
+ snprintf(remote, sizeof(remote), "openURL(%s)", uri);
+
+ argv[1] = (char *)"-remote";
+ argv[2] = remote;
+ argv[3] = 0;
+ } else if (!strcmp(commands[i], "dtaction")) {
+ // dtaction open uri
+ argv[1] = (char *)"open";
+ argv[2] = (char *)uri;
+ argv[3] = 0;
+ } else {
+ // program uri
+ argv[1] = (char *)uri;
+ argv[2] = 0;
+ }
+
+ if (msg) {
+ strlcpy(msg, argv[0], msglen);
+
+ for (i = 1; argv[i]; i ++) {
+ strlcat(msg, " ", msglen);
+ strlcat(msg, argv[i], msglen);
+ }
+ }
+
+ return run_program(command, argv, msg, msglen) != 0;
+}
+
+
+int Fl_Unix_System_Driver::file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon)
+{
+ int num_files = 0;
+#if defined(_AIX)
+ // AIX don't write the mounted filesystems to a file like '/etc/mnttab'.
+ // But reading the list of mounted filesystems from the kernel is possible:
+ // http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.basetechref/doc/basetrf1/mntctl.htm
+ int res = -1, len;
+ char *list = NULL, *name;
+ struct vmount *vp;
+
+ // We always have the root filesystem
+ add("/", icon);
+ // Get the required buffer size for the vmount structures
+ res = mntctl(MCTL_QUERY, sizeof(len), (char *) &len);
+ if (!res) {
+ // Allocate buffer ...
+ list = (char *) malloc((size_t) len);
+ if (NULL == list) {
+ res = -1;
+ } else {
+ // ... and read vmount structures from kernel
+ res = mntctl(MCTL_QUERY, len, list);
+ if (0 >= res) {
+ res = -1;
+ } else {
+ for (int i = 0, vp = (struct vmount *) list; i < res; ++i) {
+ name = (char *) vp + vp->vmt_data[VMT_STUB].vmt_off;
+ strlcpy(filename, name, lname);
+ // Skip the already added root filesystem
+ if (strcmp("/", filename) != 0) {
+ strlcat(filename, "/", lname);
+ browser->add(filename, icon);
+ }
+ vp = (struct vmount *) ((char *) vp + vp->vmt_length);
+ }
+ }
+ }
+ }
+ // Note: Executing 'free(NULL)' is allowed and simply do nothing
+ free((void *) list);
+#elif defined(__NetBSD__) && defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 300000000)
+ // NetBSD don't write the mounted filesystems to a file like '/etc/mnttab'.
+ // Since NetBSD 3.0 the system call getvfsstat(2) has replaced getfsstat(2)
+ // that is used by getmntinfo(3):
+ // http://www.daemon-systems.org/man/getmntinfo.3.html
+ int res = -1;
+ struct statvfs *list;
+
+ // We always have the root filesystem
+ browser->add("/", icon);
+# ifdef HAVE_PTHREAD
+ // Lock mutex for thread safety
+ if (!pthread_mutex_lock(&getvfsstat_mutex)) {
+# endif // HAVE_PTHREAD
+ // Get list of statvfs structures
+ res = getmntinfo(&list, ST_WAIT);
+ if (0 < res) {
+ for (int i = 0; i < res; ++i) {
+ strlcpy(filename, list[i].f_mntonname, lname);
+ // Skip the already added root filesystem
+ if (strcmp("/", filename) != 0) {
+ strlcat(filename, "/", lname);
+ browser->add(filename, icon);
+ }
+ }
+ } else {
+ res = -1;
+ }
+# ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&getvfsstat_mutex);
+ }
+# endif // HAVE_PTHREAD
+#else
+ //
+ // UNIX code uses /etc/fstab or similar...
+ //
+ FILE *mtab; // /etc/mtab or /etc/mnttab file
+ char line[FL_PATH_MAX]; // Input line
+
+ // Every Unix has a root filesystem '/'.
+ // This ensures that the user don't get an empty
+ // window after requesting filesystem list.
+ browser->add("/", icon);
+ num_files ++;
+
+ //
+ // Open the file that contains a list of mounted filesystems...
+ //
+ // Note: this misses automounted filesystems on FreeBSD if absent from /etc/fstab
+ //
+
+ mtab = fopen("/etc/mnttab", "r"); // Fairly standard
+ if (mtab == NULL)
+ mtab = fopen("/etc/mtab", "r"); // More standard
+ if (mtab == NULL)
+ mtab = fopen("/etc/fstab", "r"); // Otherwise fallback to full list
+ if (mtab == NULL)
+ mtab = fopen("/etc/vfstab", "r"); // Alternate full list file
+
+ if (mtab != NULL)
+ {
+ while (fgets(line, sizeof(line), mtab) != NULL)
+ {
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+ if (sscanf(line, "%*s%4095s", filename) != 1)
+ continue;
+ if (strcmp("/", filename) == 0)
+ continue; // "/" was added before
+
+ // Add a trailing slash (except for the root filesystem)
+ strlcat(filename, "/", lname);
+
+ // printf("Fl_File_Browser::load() - adding \"%s\" to list...\n", filename);
+ browser->add(filename, icon);
+ num_files ++;
+ }
+
+ fclose(mtab);
+ }
+#endif // _AIX || ...
+ return num_files;
+}
+
+void Fl_Unix_System_Driver::newUUID(char *uuidBuffer)
+{
+ unsigned char b[16];
+#if HAVE_DLSYM && HAVE_DLFCN_H
+ typedef void (*gener_f_type)(uchar*);
+ static bool looked_for_uuid_generate = false;
+ static gener_f_type uuid_generate_f = NULL;
+ if (!looked_for_uuid_generate) {
+ looked_for_uuid_generate = true;
+ uuid_generate_f = (gener_f_type)dlopen_or_dlsym("libuuid", "uuid_generate");
+ }
+ if (uuid_generate_f) {
+ uuid_generate_f(b);
+ } else
+#endif
+ {
+ time_t t = time(0); // first 4 byte
+ b[0] = (unsigned char)t;
+ b[1] = (unsigned char)(t>>8);
+ b[2] = (unsigned char)(t>>16);
+ b[3] = (unsigned char)(t>>24);
+ int r = rand(); // four more bytes
+ b[4] = (unsigned char)r;
+ b[5] = (unsigned char)(r>>8);
+ b[6] = (unsigned char)(r>>16);
+ b[7] = (unsigned char)(r>>24);
+ unsigned long a = (unsigned long)&t; // four more bytes
+ b[8] = (unsigned char)a;
+ b[9] = (unsigned char)(a>>8);
+ b[10] = (unsigned char)(a>>16);
+ b[11] = (unsigned char)(a>>24);
+ // Now we try to find 4 more "random" bytes. We extract the
+ // lower 4 bytes from the address of t - it is created on the
+ // stack so *might* be in a different place each time...
+ // This is now done via a union to make it compile OK on 64-bit systems.
+ union { void *pv; unsigned char a[sizeof(void*)]; } v;
+ v.pv = (void *)(&t);
+ // NOTE: May need to handle big- or little-endian systems here
+# if WORDS_BIGENDIAN
+ b[8] = v.a[sizeof(void*) - 1];
+ b[9] = v.a[sizeof(void*) - 2];
+ b[10] = v.a[sizeof(void*) - 3];
+ b[11] = v.a[sizeof(void*) - 4];
+# else // data ordered for a little-endian system
+ b[8] = v.a[0];
+ b[9] = v.a[1];
+ b[10] = v.a[2];
+ b[11] = v.a[3];
+# endif
+ char name[80]; // last four bytes
+ gethostname(name, 79);
+ memcpy(b+12, name, 4);
+ }
+ sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
+ b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
+}
+
+/*
+ Note: `prefs` can be NULL!
+ */
+char *Fl_Unix_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/,
+ Fl_Preferences::Root root,
+ const char *vendor,
+ const char *application)
+{
+ static char *filename = 0L;
+ if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX);
+ const char *home = "";
+ int pref_type = root & Fl_Preferences::ROOT_MASK;
+ switch (pref_type) {
+ case Fl_Preferences::USER:
+ home = getenv("HOME");
+ // make sure that $HOME is set to an existing directory
+ if ((home == NULL) || (home[0] == 0) || (::access(home, F_OK) == -1)) {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw)
+ home = pw->pw_dir;
+ }
+ if ((home == 0L) || (home[0] == 0) || (::access(home, F_OK) == -1))
+ return NULL;
+ strlcpy(filename, home, FL_PATH_MAX);
+ if (filename[strlen(filename) - 1] != '/')
+ strlcat(filename, "/", FL_PATH_MAX);
+ strlcat(filename, ".fltk/", FL_PATH_MAX);
+ break;
+ case Fl_Preferences::SYSTEM:
+ strcpy(filename, "/etc/fltk/");
+ break;
+ default: // MEMORY
+ filename[0] = '\0'; // empty string
+ break;
+ }
+
+ // Make sure that the parameters are not NULL
+ if ( (vendor==NULL) || (vendor[0]==0) )
+ vendor = "unknown";
+ if ( (application==NULL) || (application[0]==0) )
+ application = "unknown";
+
+ snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename),
+ "%s/%s.prefs", vendor, application);
+
+ // If this is not the USER path (i.e. SYSTEM or MEMORY), we are done
+ if ((pref_type) != Fl_Preferences::USER)
+ return filename;
+
+ // If the legacy file exists, we are also done
+ if (::access(filename, F_OK)==0)
+ return filename;
+
+ // This is USER mode, and there is no legacy file. Create an XDG conforming path.
+ // Check $XDG_CONFIG_HOME, and if it isn't set, default to $HOME/.config
+ const char *xdg = getenv("XDG_CONFIG_HOME");
+ if (xdg==NULL) {
+ xdg = "~/.config";
+ }
+ filename[0] = 0;
+ if (strncmp(xdg, "~/", 2)==0) {
+ strlcpy(filename, home, FL_PATH_MAX);
+ strlcat(filename, "/", FL_PATH_MAX);
+ strlcat(filename, xdg+2, FL_PATH_MAX);
+ } else if (strncmp(xdg, "$HOME/", 6)==0) {
+ strlcpy(filename, home, FL_PATH_MAX);
+ strlcat(filename, "/", FL_PATH_MAX);
+ strlcat(filename, xdg+6, FL_PATH_MAX);
+ } else if (strncmp(xdg, "${HOME}/", 8)==0) {
+ strlcpy(filename, home, FL_PATH_MAX);
+ strlcat(filename, "/", FL_PATH_MAX);
+ strlcat(filename, xdg+8, FL_PATH_MAX);
+ } else {
+ strlcpy(filename, xdg, FL_PATH_MAX);
+ }
+ strlcat(filename, "/", FL_PATH_MAX);
+ strlcat(filename, vendor, FL_PATH_MAX);
+ strlcat(filename, "/", FL_PATH_MAX);
+ strlcat(filename, application, FL_PATH_MAX);
+ strlcat(filename, ".prefs", FL_PATH_MAX);
+
+ return filename;
+}
+
+//
+// Needs some docs
+// Returns -1 on error, errmsg will contain OS error if non-NULL.
+//
+int Fl_Unix_System_Driver::filename_list(const char *d,
+ dirent ***list,
+ int (*sort)(struct dirent **, struct dirent **),
+ char *errmsg, int errmsg_sz) {
+ int dirlen;
+ char *dirloc;
+
+ if (errmsg && errmsg_sz>0) errmsg[0] = '\0';
+
+ // Assume that locale encoding is no less dense than UTF-8
+ dirlen = strlen(d);
+ dirloc = (char *)malloc(dirlen + 1);
+ fl_utf8to_mb(d, dirlen, dirloc, dirlen + 1);
+
+#ifndef HAVE_SCANDIR
+ // This version is when we define our own scandir. Note it updates errmsg on errors.
+ int n = fl_scandir(dirloc, list, 0, sort, errmsg, errmsg_sz);
+#elif defined(HAVE_SCANDIR_POSIX)
+ // POSIX (2008) defines the comparison function like this:
+ int n = scandir(dirloc, list, 0, (int(*)(const dirent **, const dirent **))sort);
+#elif defined(__osf__)
+ // OSF, DU 4.0x
+ int n = scandir(dirloc, list, 0, (int(*)(dirent **, dirent **))sort);
+#elif defined(_AIX)
+ // AIX is almost standard...
+ int n = scandir(dirloc, list, 0, (int(*)(void*, void*))sort);
+#elif defined(__sgi)
+ int n = scandir(dirloc, list, 0, sort);
+#else
+ // The vast majority of UNIX systems want the sort function to have this
+ // prototype, most likely so that it can be passed to qsort without any
+ // changes:
+ int n = scandir(dirloc, list, 0, (int(*)(const void*,const void*))sort);
+#endif
+
+ free(dirloc);
+
+ if (n==-1) {
+ // Don't write to errmsg if FLTK's fl_scandir() already set it.
+ // If OS's scandir() was used (HAVE_SCANDIR), we return its error in errmsg here..
+#ifdef HAVE_SCANDIR
+ if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno));
+#endif
+ return -1;
+ }
+
+ // convert every filename to UTF-8, and append a '/' to all
+ // filenames that are directories
+ int i;
+ char *fullname = (char*)malloc(dirlen+FL_PATH_MAX+3); // Add enough extra for two /'s and a nul
+ // Use memcpy for speed since we already know the length of the string...
+ memcpy(fullname, d, dirlen+1);
+
+ char *name = fullname + dirlen;
+ if (name!=fullname && name[-1]!='/')
+ *name++ = '/';
+
+ for (i=0; i<n; i++) {
+ int newlen;
+ dirent *de = (*list)[i];
+ int len = strlen(de->d_name);
+ newlen = fl_utf8from_mb(NULL, 0, de->d_name, len);
+ dirent *newde = (dirent*)malloc(de->d_name - (char*)de + newlen + 2); // Add space for a / and a nul
+
+ // Conversion to UTF-8
+ memcpy(newde, de, de->d_name - (char*)de);
+ fl_utf8from_mb(newde->d_name, newlen + 1, de->d_name, len);
+
+ // Check if dir (checks done on "old" name as we need to interact with
+ // the underlying OS)
+ if (de->d_name[len-1]!='/' && len<=FL_PATH_MAX) {
+ // Use memcpy for speed since we already know the length of the string...
+ memcpy(name, de->d_name, len+1);
+ if (fl_filename_isdir(fullname)) {
+ char *dst = newde->d_name + newlen;
+ *dst++ = '/';
+ *dst = 0;
+ }
+ }
+
+ free(de);
+ (*list)[i] = newde;
+ }
+ free(fullname);
+
+ return n;
+}
+
+int Fl_Unix_System_Driver::utf8locale() {
+ static int ret = 2;
+ if (ret == 2) {
+ char* s;
+ ret = 1; /* assume UTF-8 if no locale */
+ if (((s = getenv("LC_CTYPE")) && *s) ||
+ ((s = getenv("LC_ALL")) && *s) ||
+ ((s = getenv("LANG")) && *s)) {
+ ret = (strstr(s,"utf") || strstr(s,"UTF"));
+ }
+ }
+ return ret;
+}
+
+
+// returns pointer to the filename, or null if name ends with '/'
+const char *Fl_Unix_System_Driver::filename_name(const char *name) {
+ const char *p,*q;
+ if (!name) return (0);
+ for (p=q=name; *p;) if (*p++ == '/') q = p;
+ return q;
+}
+
+
+////////////////////////////////////////////////////////////////
+// interface to poll/select call:
+
+# if USE_POLL
+
+# include <poll.h>
+static pollfd *pollfds = 0;
+
+# else
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif /* HAVE_SYS_SELECT_H */
+
+// The following #define is only needed for HP-UX 9.x and earlier:
+//#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
+
+static fd_set fdsets[3];
+static int maxfd;
+# define POLLIN 1
+# define POLLOUT 4
+# define POLLERR 8
+
+# endif /* USE_POLL */
+
+static int nfds = 0;
+static int fd_array_size = 0;
+struct FD {
+# if !USE_POLL
+ int fd;
+ short events;
+# endif
+ void (*cb)(int, void*);
+ void* arg;
+};
+
+static FD *fd = 0;
+
+void Fl_Unix_System_Driver::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
+ remove_fd(n,events);
+ int i = nfds++;
+ if (i >= fd_array_size) {
+ FD *temp;
+ fd_array_size = 2*fd_array_size+1;
+
+ if (!fd) temp = (FD*)malloc(fd_array_size*sizeof(FD));
+ else temp = (FD*)realloc(fd, fd_array_size*sizeof(FD));
+
+ if (!temp) return;
+ fd = temp;
+
+# if USE_POLL
+ pollfd *tpoll;
+
+ if (!pollfds) tpoll = (pollfd*)malloc(fd_array_size*sizeof(pollfd));
+ else tpoll = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
+
+ if (!tpoll) return;
+ pollfds = tpoll;
+# endif
+ }
+ fd[i].cb = cb;
+ fd[i].arg = v;
+# if USE_POLL
+ pollfds[i].fd = n;
+ pollfds[i].events = events;
+# else
+ fd[i].fd = n;
+ fd[i].events = events;
+ if (events & POLLIN) FD_SET(n, &fdsets[0]);
+ if (events & POLLOUT) FD_SET(n, &fdsets[1]);
+ if (events & POLLERR) FD_SET(n, &fdsets[2]);
+ if (n > maxfd) maxfd = n;
+# endif
+}
+
+void Fl_Unix_System_Driver::add_fd(int n, void (*cb)(int, void*), void* v) {
+ add_fd(n, POLLIN, cb, v);
+}
+
+void Fl_Unix_System_Driver::remove_fd(int n, int events) {
+ int i,j;
+# if !USE_POLL
+ maxfd = -1; // recalculate maxfd on the fly
+# endif
+ for (i=j=0; i<nfds; i++) {
+# if USE_POLL
+ if (pollfds[i].fd == n) {
+ int e = pollfds[i].events & ~events;
+ if (!e) continue; // if no events left, delete this fd
+ pollfds[j].events = e;
+ }
+# else
+ if (fd[i].fd == n) {
+ int e = fd[i].events & ~events;
+ if (!e) continue; // if no events left, delete this fd
+ fd[i].events = e;
+ }
+ if (fd[i].fd > maxfd) maxfd = fd[i].fd;
+# endif
+ // move it down in the array if necessary:
+ if (j<i) {
+ fd[j] = fd[i];
+# if USE_POLL
+ pollfds[j] = pollfds[i];
+# endif
+ }
+ j++;
+ }
+ nfds = j;
+# if !USE_POLL
+ if (events & POLLIN) FD_CLR(n, &fdsets[0]);
+ if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
+ if (events & POLLERR) FD_CLR(n, &fdsets[2]);
+# endif
+}
+
+void Fl_Unix_System_Driver::remove_fd(int n) {
+ remove_fd(n, -1);
+}
+
+
+// these pointers are set by the Fl::lock() function:
+static void nothing() {}
+void (*fl_lock_function)() = nothing;
+void (*fl_unlock_function)() = nothing;
+
+
+// This is never called with time_to_wait < 0.0:
+// It should return negative on error, 0 if nothing happens before
+// timeout, and >0 if any callbacks were done.
+int Fl_Unix_System_Driver::poll_or_select_with_delay(double time_to_wait) {
+# if !USE_POLL
+ fd_set fdt[3];
+ fdt[0] = fdsets[0];
+ fdt[1] = fdsets[1];
+ fdt[2] = fdsets[2];
+# endif
+ int n;
+
+ fl_unlock_function();
+
+ if (time_to_wait < 2147483.648) {
+# if USE_POLL
+ n = ::poll(pollfds, nfds, int(time_to_wait*1000 + .5));
+# else
+ timeval t;
+ t.tv_sec = int(time_to_wait);
+ t.tv_usec = int(1000000 * (time_to_wait-t.tv_sec));
+ n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
+# endif
+ } else {
+# if USE_POLL
+ n = ::poll(pollfds, nfds, -1);
+# else
+ n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
+# endif
+ }
+
+ fl_lock_function();
+
+ if (n > 0) {
+ for (int i=0; i<nfds; i++) {
+# if USE_POLL
+ if (pollfds[i].revents) fd[i].cb(pollfds[i].fd, fd[i].arg);
+# else
+ int f = fd[i].fd;
+ short revents = 0;
+ if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
+ if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
+ if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
+ if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
+# endif
+ }
+ }
+ return n;
+}
+
+int Fl_Unix_System_Driver::poll_or_select() {
+ if (!nfds) return 0; // nothing to select or poll
+# if USE_POLL
+ return ::poll(pollfds, nfds, 0);
+# else
+ timeval t;
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ fd_set fdt[3];
+ fdt[0] = fdsets[0];
+ fdt[1] = fdsets[1];
+ fdt[2] = fdsets[2];
+ return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
+# endif
+}
+
+
+double Fl_Unix_System_Driver::wait(double time_to_wait)
+{
+ if (time_to_wait <= 0.0) {
+ // do flush second so that the results of events are visible:
+ int ret = this->poll_or_select_with_delay(0.0);
+ Fl::flush();
+ return ret;
+ } else {
+ // do flush first so that user sees the display:
+ Fl::flush();
+ if (Fl::idle) // 'idle' may have been set within flush()
+ time_to_wait = 0.0;
+ else {
+ Fl_Timeout::elapse_timeouts();
+ time_to_wait = Fl_Timeout::time_to_wait(time_to_wait);
+ }
+ return this->poll_or_select_with_delay(time_to_wait);
+ }
+}
+
+int Fl_Unix_System_Driver::ready()
+{
+ Fl_Timeout::elapse_timeouts();
+ if (Fl_Timeout::time_to_wait(1.0) <= 0.0) return 1;
+ return this->poll_or_select();
+}
+
+
+static void write_short(unsigned char **cp, short i) {
+ unsigned char *c = *cp;
+ *c++ = i & 0xFF; i >>= 8;
+ *c++ = i & 0xFF;
+ *cp = c;
+}
+
+static void write_int(unsigned char **cp, int i) {
+ unsigned char *c = *cp;
+ *c++ = i & 0xFF; i >>= 8;
+ *c++ = i & 0xFF; i >>= 8;
+ *c++ = i & 0xFF; i >>= 8;
+ *c++ = i & 0xFF;
+ *cp = c;
+}
+
+
+unsigned char *Fl_Unix_System_Driver::create_bmp(const unsigned char *data, int W, int H, int *return_size) {
+ int R = ((3*W+3)/4) * 4; // the number of bytes per row, rounded up to multiple of 4
+ int s=H*R;
+ int fs=14+40+s;
+ unsigned char *b=new unsigned char[fs];
+ unsigned char *c=b;
+ // BMP header
+ *c++='B';
+ *c++='M';
+ write_int(&c,fs);
+ write_int(&c,0);
+ write_int(&c,14+40);
+ // DIB header:
+ write_int(&c,40);
+ write_int(&c,W);
+ write_int(&c,H);
+ write_short(&c,1);
+ write_short(&c,24);//bits ber pixel
+ write_int(&c,0);//RGB
+ write_int(&c,s);
+ write_int(&c,0);// horizontal resolution
+ write_int(&c,0);// vertical resolution
+ write_int(&c,0);//number of colors. 0 -> 1<<bits_per_pixel
+ write_int(&c,0);
+ // Pixel data
+ data+=3*W*H;
+ for (int y=0;y<H;++y){
+ data-=3*W;
+ const unsigned char *s=data;
+ unsigned char *p=c;
+ for (int x=0;x<W;++x){
+ *p++=s[2];
+ *p++=s[1];
+ *p++=s[0];
+ s+=3;
+ }
+ c+=R;
+ }
+ *return_size = fs;
+ return b;
+}
+
+
+static void read_int(uchar *c, int& i) {
+ i = *c;
+ i |= (*(++c))<<8;
+ i |= (*(++c))<<16;
+ i |= (*(++c))<<24;
+}
+
+
+// turn BMP image FLTK produced by create_bmp() back to Fl_RGB_Image
+Fl_RGB_Image *Fl_Unix_System_Driver::own_bmp_to_RGB(char *bmp) {
+ int w, h;
+ read_int((uchar*)bmp + 18, w);
+ read_int((uchar*)bmp + 22, h);
+ int R=((3*w+3)/4) * 4; // the number of bytes per row, rounded up to multiple of 4
+ bmp += 54;
+ uchar *data = new uchar[w*h*3];
+ uchar *p = data;
+ for (int i = h-1; i >= 0; i--) {
+ char *s = bmp + i * R;
+ for (int j = 0; j < w; j++) {
+ *p++=s[2];
+ *p++=s[1];
+ *p++=s[0];
+ s+=3;
+ }
+ }
+ Fl_RGB_Image *img = new Fl_RGB_Image(data, w, h, 3);
+ img->alloc_array = 1;
+ return img;
+}
+
+
+void *Fl_Unix_System_Driver::control_maximize_button(void *data) {
+ return NULL;
+}
diff --git a/src/drivers/Wayland/Fl_Font.H b/src/drivers/Wayland/Fl_Font.H
new file mode 100644
index 000000000..eca2f32ca
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Font.H
@@ -0,0 +1,33 @@
+//
+// Font definitions for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2021 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_FONT_
+#define FL_FONT_
+
+#include <config.h>
+#include "Fl_Wayland_Graphics_Driver.H"
+
+class Fl_Wayland_Font_Descriptor : public Fl_Font_Descriptor {
+public:
+ Fl_Wayland_Font_Descriptor(const char* fontname, Fl_Fontsize size);
+ FL_EXPORT ~Fl_Wayland_Font_Descriptor();
+ PangoFontDescription *fontref;
+ int **width; // array of arrays of character widths
+};
+
+extern FL_EXPORT Fl_Fontdesc *fl_fonts; // the table
+
+#endif // FL_FONT_
diff --git a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx
new file mode 100644
index 000000000..addcfbebd
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx
@@ -0,0 +1,74 @@
+//
+// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2021 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#include <FL/Fl_Copy_Surface.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"
+#include <FL/platform.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();
+ void translate(int x, int y);
+ void untranslate();
+};
+
+
+Fl_Copy_Surface_Driver *Fl_Copy_Surface_Driver::newCopySurfaceDriver(int w, int h)
+{
+ return new Fl_Wayland_Copy_Surface_Driver(w, h);
+}
+
+
+Fl_Wayland_Copy_Surface_Driver::Fl_Wayland_Copy_Surface_Driver(int w, int h) : Fl_Copy_Surface_Driver(w, h) {
+ int os_scale = (fl_window ? fl_window->scale : 1);
+ img_surf = new Fl_Image_Surface(w * os_scale, h * os_scale);
+ driver(img_surf->driver());
+ driver()->scale(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_Wayland_Graphics_Driver*)driver())->activate(img_surf->offscreen(), driver()->scale());
+}
+
+
+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.cxx b/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx
new file mode 100644
index 000000000..290a52361
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx
@@ -0,0 +1,401 @@
+//
+// Class Fl_Wayland_Gl_Window_Driver for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2021-2022 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#if HAVE_GL
+#include <FL/platform.H>
+#include <FL/Fl_Image_Surface.H>
+#include "../../Fl_Gl_Choice.H"
+#include "../../Fl_Screen_Driver.H"
+#include "Fl_Wayland_Window_Driver.H"
+#include "Fl_Wayland_Graphics_Driver.H"
+#include "../../Fl_Gl_Window_Driver.H"
+#include <wayland-egl.h>
+#include <EGL/egl.h>
+#include <FL/gl.h>
+
+/* Implementation note 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.
+*/
+
+class Fl_Wayland_Gl_Window_Driver : public Fl_Gl_Window_Driver {
+ friend class Fl_Gl_Window_Driver;
+ bool egl_resize_in_progress;
+protected:
+ Fl_Wayland_Gl_Window_Driver(Fl_Gl_Window *win);
+ virtual float pixels_per_unit();
+ virtual void make_current_before();
+ virtual int mode_(int m, const int *a);
+ virtual void swap_buffers();
+ virtual void resize(int is_a_resize, int w, int h);
+ virtual char swap_type();
+ virtual Fl_Gl_Choice *find(int m, const int *alistp);
+ virtual GLContext create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g, int layer = 0);
+ virtual void set_gl_context(Fl_Window* w, GLContext context);
+ virtual void delete_gl_context(GLContext);
+ virtual void make_overlay_current();
+ virtual void redraw_overlay();
+ virtual void waitGL();
+ virtual void gl_start();
+ virtual Fl_RGB_Image* capture_gl_rectangle(int x, int y, int w, int h);
+ char *alpha_mask_for_string(const char *str, int n, int w, int h, Fl_Fontsize fs);
+public:
+ static EGLDisplay egl_display;
+ static EGLint configs_count;
+ static struct wl_event_queue *gl_event_queue;
+ void init();
+ struct wl_egl_window *egl_window;
+ EGLSurface egl_surface;
+};
+
+// 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;
+ }
+};
+
+EGLDisplay Fl_Wayland_Gl_Window_Driver::egl_display = EGL_NO_DISPLAY;
+EGLint Fl_Wayland_Gl_Window_Driver::configs_count = 0;
+struct wl_event_queue *Fl_Wayland_Gl_Window_Driver::gl_event_queue = NULL;
+
+
+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;
+ egl_resize_in_progress = false;
+}
+
+
+void Fl_Wayland_Gl_Window_Driver::init() {
+ EGLint major, minor;
+
+ if (!fl_display) Fl::screen_driver()->open_display();
+ egl_display = eglGetDisplay((EGLNativeDisplayType) fl_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);
+
+ gl_event_queue = wl_display_create_queue(fl_display);
+}
+
+
+char *Fl_Wayland_Gl_Window_Driver::alpha_mask_for_string(const char *str, int n, int w, int h, Fl_Fontsize fs)
+{
+ // write str to a bitmap just big enough
+ Window save_win = fl_window;
+ fl_window = NULL;
+ Fl_Image_Surface *surf = new Fl_Image_Surface(w, h);
+ fl_window = save_win;
+ Fl_Font f=fl_font();
+ Fl_Surface_Device::push_current(surf);
+ fl_color(FL_BLACK);
+ fl_rectf(0, 0, w, h);
+ fl_color(FL_WHITE);
+ fl_font(f, fs);
+ fl_draw(str, n, 0, fl_height() - fl_descent());
+ // get the R channel only of the bitmap
+ char *alpha_buf = new char[w*h], *r = alpha_buf, *q;
+ for (int i = 0; i < h; i++) {
+ q = (char*)surf->offscreen()->draw_buffer + i * surf->offscreen()->stride;
+ for (int j = 0; j < w; j++) {
+ *r++ = *q;
+ q += 4;
+ }
+ }
+ Fl_Surface_Device::pop_current();
+ delete surf;
+ return alpha_buf;
+}
+
+
+Fl_Gl_Choice *Fl_Wayland_Gl_Window_Driver::find(int m, const int *alistp)
+{
+ m |= FL_DOUBLE;
+ 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_NONE
+ };
+
+ if (m & FL_DEPTH) config_attribs[11] = 1;
+ if (m & FL_MULTISAMPLE) config_attribs[13] = 1;
+ if (m & FL_STENCIL) config_attribs[15] = 1;
+
+ static EGLConfig *configs = (void**)calloc(configs_count, sizeof(EGLConfig));
+ eglChooseConfig(egl_display, config_attribs, configs, configs_count, &n);
+ if (n == 0 && (m & FL_MULTISAMPLE)) {
+ config_attribs[13] = 0;
+ eglChooseConfig(egl_display, config_attribs, configs, configs_count, &n);
+ }
+ if (n == 0) {
+ Fl::fatal("failed to choose an EGL config\n");
+ }
+
+ g = new Fl_Wayland_Gl_Choice(m, alistp, first);
+ /*for (int i = 0; i < n; i++) {
+ EGLint size;
+ eglGetConfigAttrib(egl_display, configs[i], EGL_BUFFER_SIZE, &size);
+ printf("Buffer size for config %d is %d\n", i, size);
+ eglGetConfigAttrib(egl_display, configs[i], EGL_RED_SIZE, &size);
+ printf("Red size for config %d is %d\n", i, size);
+ // just choose the first one
+ g->egl_conf = configs[i];
+ break;
+ }*/
+ // just choose the first config
+ g->egl_conf = configs[0];
+ first = g;
+ return g;
+}
+
+
+GLContext Fl_Wayland_Gl_Window_Driver::create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g, int layer) {
+ 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?shared_ctx:EGL_NO_CONTEXT, context_attribs);
+//fprintf(stderr, "eglCreateContext=%p shared_ctx=%p\n", ctx, shared_ctx);
+ if (ctx)
+ add_context(ctx);
+ return ctx;
+}
+
+
+void Fl_Wayland_Gl_Window_Driver::set_gl_context(Fl_Window* w, GLContext context) {
+ struct wld_window *win = fl_xid(w);
+ if (!win || !egl_surface) return;
+ if (context != cached_context || w != cached_window) {
+ cached_context = context;
+ cached_window = w;
+ if (eglMakeCurrent(egl_display, egl_surface, egl_surface, (EGLContext)context)) {
+//fprintf(stderr, "EGLContext %p made current\n", context);
+ } else {
+ Fl::error("eglMakeCurrent() failed\n");
+ }
+ }
+}
+
+void Fl_Wayland_Gl_Window_Driver::delete_gl_context(GLContext context) {
+ if (cached_context == context) {
+ cached_context = 0;
+ cached_window = 0;
+ }
+//EGLBoolean b =
+ eglDestroyContext(egl_display, context);
+//fprintf(stderr,"EGL context %p destroyed %s\n", context, b==EGL_TRUE?"successfully":"w/ error");
+//b =
+ eglDestroySurface(egl_display, egl_surface);
+//fprintf(stderr,"EGLSurface %p destroyed %s\n", egl_surface, b==EGL_TRUE?"successfully":"w/ error");
+ egl_surface = NULL;
+ wl_egl_window_destroy(egl_window);
+ egl_window = NULL;
+ del_context(context);
+}
+
+
+void Fl_Wayland_Gl_Window_Driver::make_overlay_current() {
+//fprintf(stderr, "make_overlay_current\n");
+ glDrawBuffer(GL_FRONT);
+}
+
+void Fl_Wayland_Gl_Window_Driver::redraw_overlay() {
+//fprintf(stderr, "redraw_overlay\n");
+ pWindow->redraw();
+}
+
+
+Fl_Gl_Window_Driver *Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *w)
+{
+ return new Fl_Wayland_Gl_Window_Driver(w);
+}
+
+
+static void gl_frame_ready(void *data, struct wl_callback *cb, uint32_t time) {
+ *(bool*)data = true;
+}
+
+
+static const struct wl_callback_listener gl_surface_frame_listener = {
+ .done = gl_frame_ready,
+};
+
+
+void Fl_Wayland_Gl_Window_Driver::make_current_before() {
+ if (!egl_window) {
+ struct wld_window *win = fl_xid(pWindow);
+ struct wl_surface *surface = win->wl_surface;
+ egl_window = wl_egl_window_create(surface, pWindow->pixel_w(), pWindow->pixel_h());
+ if (egl_window == EGL_NO_SURFACE) {
+ Fl::fatal("Can't create egl window with wl_egl_window_create()\n");
+ } else {
+ //fprintf(stderr, "Created egl window=%p\n", egl_window);
+ }
+ Fl_Wayland_Gl_Choice *g = (Fl_Wayland_Gl_Choice*)this->g();
+ egl_surface = eglCreateWindowSurface(egl_display, g->egl_conf, egl_window, NULL);
+//fprintf(stderr, "Created egl surface=%p at scale=%d\n", egl_surface, win->scale);
+ wl_surface_set_buffer_scale(surface, win->scale);
+ if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::WESTON) {
+ bool done = false;
+ struct wl_callback *callback = wl_surface_frame(surface);
+ wl_surface_commit(surface);
+ wl_callback_add_listener(callback, &gl_surface_frame_listener, &done);
+ while (!done) wl_display_dispatch(fl_display);
+ }
+ }
+}
+
+
+float Fl_Wayland_Gl_Window_Driver::pixels_per_unit()
+{
+ int ns = Fl_Window_Driver::driver(pWindow)->screen_num();
+ int wld_scale = pWindow->shown() ? fl_xid(pWindow)->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::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 Gl_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) {
+ //eglSwapInterval(egl_display, 0); // doesn't sem to have any effect in this context
+ if (!egl_resize_in_progress) {
+ while (wl_display_prepare_read(fl_display) != 0) {
+ wl_display_dispatch_pending(fl_display);
+ }
+ wl_display_read_events(fl_display);
+ wl_display_dispatch_queue_pending(fl_display, gl_event_queue);
+ }
+ egl_resize_in_progress = false;
+ eglSwapBuffers(Fl_Wayland_Gl_Window_Driver::egl_display, egl_surface);
+ }
+}
+
+
+class Fl_Wayland_Gl_Plugin : public Fl_Wayland_Plugin {
+public:
+ Fl_Wayland_Gl_Plugin() : Fl_Wayland_Plugin(name()) { }
+ virtual const char *name() { return "gl.wayland.fltk.org"; }
+ virtual void do_swap(Fl_Window *w) {
+ Fl_Gl_Window_Driver *gldr = Fl_Gl_Window_Driver::driver(w->as_gl_window());
+ if (gldr->overlay() == w) gldr->swap_buffers();
+ }
+ virtual void invalidate(Fl_Window *w) {
+ w->as_gl_window()->valid(0);
+ }
+};
+
+static Fl_Wayland_Gl_Plugin Gl_Overlay_Plugin;
+
+
+void Fl_Wayland_Gl_Window_Driver::resize(int is_a_resize, int W, int H) {
+ if (!egl_window) return;
+ struct wld_window *win = fl_xid(pWindow);
+ float f = Fl::screen_scale(pWindow->screen_num());
+ W = (W * win->scale) * f;
+ H = (H * win->scale) * f;
+ int W2, H2;
+ wl_egl_window_get_attached_size(egl_window, &W2, &H2);
+ if (W2 != W || H2 != H) {
+ wl_egl_window_resize(egl_window, W, H, 0, 0);
+ //fprintf(stderr, "Fl_Wayland_Gl_Window_Driver::resize to %dx%d\n", W, H);
+ egl_resize_in_progress = true;
+ }
+}
+
+char Fl_Wayland_Gl_Window_Driver::swap_type() {
+ return copy;
+}
+
+void Fl_Wayland_Gl_Window_Driver::waitGL() {
+}
+
+void Fl_Wayland_Gl_Window_Driver::gl_start() {
+}
+
+
+Fl_RGB_Image* Fl_Wayland_Gl_Window_Driver::capture_gl_rectangle(int x, int y, int w, int h) {
+ Fl_Surface_Device::push_current(Fl_Display_Device::display_device());
+ Fl_RGB_Image *rgb = Fl_Gl_Window_Driver::capture_gl_rectangle(x, y, w, h);
+ Fl_Surface_Device::pop_current();
+ return rgb;
+}
+
+#endif // HAVE_GL
diff --git a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H b/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H
new file mode 100644
index 000000000..19991c531
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H
@@ -0,0 +1,158 @@
+//
+// Definition of class Fl_Wayland_Graphics_Driver.
+//
+// Copyright 2021-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
+//
+
+/**
+ \file Fl_Wayland_Graphics_Driver.H
+ \brief Definition of Wayland graphics driver.
+ */
+
+#ifndef FL_WAYLAND_GRAPHICS_DRIVER_H
+#define FL_WAYLAND_GRAPHICS_DRIVER_H
+
+
+/* Implementation note about buffers FLTK uses to support display graphics under Wayland.
+
+ Each window is associated to an FLTK-defined object of type struct wld_window
+ containing itself an FLTK-defined struct fl_wld_buffer object holding all graphics data.
+ Among members of this latter structure are:
+ - struct wl_buffer wl_buffer
+ is a Wayland-defined type for a graphics buffer able to be attached to a wl_surface;
+ - void *data
+ points to the beginning of the memory zone where wl_buffer stores its graphics data;
+ - unsigned char *draw_buffer
+ contains a graphics buffer to which all Cairo drawings are directed;
+ draw_buffer and data both have the same organization called CAIRO_FORMAT_ARGB32 in Cairo parlance
+ and WL_SHM_FORMAT_ARGB8888 in Wayland parlance which means BGRA byte order.
+ - int width
+ gives the pixel width of the graphics buffer;
+ - int stride
+ gives the stride of this buffer;
+ - size_t data_size
+ gives the total buffer size in bytes (thus, data_size / stride gives the buffer height);
+ - bool draw_buffer_needs_commit
+ is TRUE when draw_buffer has been modified and needs being committed for display, and
+ FALSE after having been committed but before having been modified;
+ - struct wl_callback *cb
+ is used to synchronize drawing with the compositor during progressive drawing.
+
+ When a graphics scene is to be committed, the data_size bytes of draw_buffer are copied by memcpy()
+ starting at data, and wl_buffer is attached to the wl_surface which is committed for display
+ by wl_surface_commit(). Finally, draw_buffer_needs_commit is set to FALSE.
+
+ All drawing functions have Cairo write to draw_buffer and turn draw_buffer_needs_commit to TRUE.
+*/
+
+
+#include "../Cairo/Fl_Cairo_Graphics_Driver.H"
+#include <cairo/cairo.h>
+#include <stdint.h> // for uint32_t
+typedef struct _PangoLayout PangoLayout;
+
+struct fl_wld_buffer {
+ struct wl_buffer *wl_buffer;
+ void *data;
+ size_t data_size; // of wl_buffer and draw_buffer
+ int stride;
+ int width;
+ unsigned char *draw_buffer;
+ struct wl_callback *cb;
+ bool draw_buffer_needs_commit;
+ cairo_t *cairo_;
+ PangoLayout *pango_layout_;
+};
+struct wld_window;
+
+class FL_EXPORT Fl_Wayland_Graphics_Driver : public Fl_Cairo_Graphics_Driver {
+private:
+ struct fl_wld_buffer *buffer_;
+ PangoLayout *dummy_pango_layout_; // used to measure text width before showing a window
+ int linestyle_;
+ void draw_cached_pattern_(Fl_Image *img, cairo_pattern_t *pat, int X, int Y, int W, int H, int cx, int cy);
+public:
+ Fl_Wayland_Graphics_Driver();
+ ~Fl_Wayland_Graphics_Driver();
+ static const uint32_t wld_format;
+ static const cairo_format_t cairo_format;
+ void activate(struct fl_wld_buffer *buffer, float scale);
+ void font(Fl_Font fnum, Fl_Fontsize s);
+ Fl_Font font() { return Fl_Graphics_Driver::font(); }
+ void draw(const char* s, int nBytes, int x, int y) { draw(s, nBytes, float(x), float(y)); }
+ void draw(const char* s, int nBytes, float x, float y);
+ void draw(int angle, const char *str, int n, int x, int y);
+ void rtl_draw(const char* str, int n, int x, int y);
+ int height();
+ int descent();
+ double width(const char *str, int n);
+ double width(unsigned c);
+ void text_extents(const char* txt, int n, int& dx, int& dy, int& w, int& h);
+ int not_clipped(int x, int y, int w, int h);
+ int clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H);
+ void restore_clip();
+ void clip_region(Fl_Region r);
+ void line_style(int style, int width=0, char* dashes=0);
+ Fl_Region XRectangleRegion(int x, int y, int w, int h);
+ void add_rectangle_to_region(Fl_Region r, int X, int Y, int W, int H);
+ void XDestroyRegion(Fl_Region r);
+ void set_color(Fl_Color i, unsigned c);
+ Fl_Font set_fonts(const char* pattern_name);
+ const char *font_name(int num);
+ void font_name(int num, const char *name);
+ const char* get_font_name(Fl_Font fnum, int* ap);
+ int get_font_sizes(Fl_Font fnum, int*& sizep);
+ void point(int x, int y);
+ void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen osrc, int srcx, int srcy);
+ void draw_image(const uchar *data, int ix, int iy, int iw, int ih, int D, int LD);
+ void curve(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3);
+ void begin_points();
+ void end_points();
+ void transformed_vertex(double x, double y);
+ void draw_rgb(Fl_RGB_Image *rgb,int XP, int YP, int WP, int HP, int cx, int cy);
+ void cache(Fl_RGB_Image *rgb);
+ void uncache(Fl_RGB_Image *img, fl_uintptr_t &id_, fl_uintptr_t &mask_);
+ void draw_bitmap(Fl_Bitmap *bm,int XP, int YP, int WP, int HP, int cx, int cy);
+ void cache(Fl_Bitmap *img);
+ void delete_bitmask(Fl_Bitmask bm);
+ void cache(Fl_Pixmap *pxm);
+ void draw_pixmap(Fl_Pixmap *rgb,int XP, int YP, int WP, int HP, int cx, int cy);
+ void uncache_pixmap(fl_uintptr_t p);
+ void overlay_rect(int x, int y, int w , int h);
+ static void init_built_in_fonts();
+ static struct fl_wld_buffer *create_shm_buffer(int width, int height);
+ static void buffer_release(struct wld_window *window);
+ static void buffer_commit(struct wld_window *window);
+ static void cairo_init(struct fl_wld_buffer *buffer, int width, int height, int stride, cairo_format_t format);
+ void line(int x1, int y1, int x2, int y2);
+ void line(int x1, int y1, int x2, int y2, int x3, int y3);
+ void xyline(int x, int y, int x1);
+ void xyline(int x, int y, int x1, int y2);
+ void xyline(int x, int y, int x1, int y2, int x3);
+ void yxline(int x, int y, int y1);
+ void yxline(int x, int y, int y1, int x2);
+ void yxline(int x, int y, int y1, int x2, int y3);
+ void loop(int x0, int y0, int x1, int y1, int x2, int y2);
+ void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);
+ void rect(int x, int y, int w, int h);
+ void rectf(int x, int y, int w, int h);
+ void polygon(int x0, int y0, int x1, int y1, int x2, int y2);
+ void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);
+ void end_loop();
+ void end_line();
+ void end_polygon();
+ void set_spot(int font, int height, int x, int y, int w, int h, Fl_Window *win);
+ void reset_spot();
+};
+
+#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
new file mode 100644
index 000000000..55f6de8d9
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx
@@ -0,0 +1,1039 @@
+//
+// Implementation of the Wayland graphics driver.
+//
+// Copyright 2021-2022 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#include <FL/platform.H>
+#include "Fl_Wayland_Graphics_Driver.H"
+#include "Fl_Wayland_Screen_Driver.H"
+#include "Fl_Wayland_Window_Driver.H"
+#include "Fl_Font.H"
+#include "text-input-client-protocol.h"
+#include <pango/pangocairo.h>
+#if ! PANGO_VERSION_CHECK(1,22,0)
+# error "Requires Pango 1.22 or higher"
+#endif
+#define _GNU_SOURCE 1
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+extern unsigned fl_cmap[256]; // defined in fl_color.cxx
+
+
+static int create_anonymous_file(int size, char **pshared)
+{
+ int ret;
+ int fd = memfd_create("FLTK-for-Wayland", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (fd < 0) {
+ Fl::fatal("memfd_create failed: %s\n", strerror(errno));
+ }
+ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ do {
+ ret = posix_fallocate(fd, 0, size);
+ } while (ret == EINTR);
+ if (ret != 0) {
+ close(fd);
+ errno = ret;
+ Fl::fatal("creating anonymous file of size %d failed: %s\n", size, strerror(errno));
+ }
+ *pshared = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (*pshared == MAP_FAILED) {
+ close(fd);
+ Fl::fatal("mmap failed: %s\n", strerror(errno));
+ }
+//printf("create_anonymous_file: %d\n",size);
+ return fd;
+}
+
+
+struct fl_wld_buffer *Fl_Wayland_Graphics_Driver::create_shm_buffer(int width, int height)
+{
+ struct fl_wld_buffer *buffer;
+ int stride = cairo_format_stride_for_width(Fl_Wayland_Graphics_Driver::cairo_format, width);
+ int size = stride * height;
+ static char *pool_memory = NULL;
+ static int pool_size = 10000000; // gets increased if necessary
+ static int chunk_offset = pool_size;
+ static int fd = -1;
+ static struct wl_shm_pool *pool = NULL;
+ if (chunk_offset + size > pool_size) {
+ chunk_offset = 0;
+ if (pool) {
+ wl_shm_pool_destroy(pool);
+ close(fd);
+ }
+ if (size > pool_size) pool_size = 2 * size;
+ fd = create_anonymous_file(pool_size, &pool_memory);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ pool = wl_shm_create_pool(scr_driver->wl_shm, fd, pool_size);
+ }
+ buffer = (struct fl_wld_buffer*)calloc(1, sizeof(struct fl_wld_buffer));
+ buffer->stride = stride;
+ buffer->wl_buffer = wl_shm_pool_create_buffer(pool, chunk_offset, width, height, stride, Fl_Wayland_Graphics_Driver::wld_format);
+ buffer->data = (void*)(pool_memory + chunk_offset);
+ chunk_offset += size;
+ buffer->data_size = size;
+ buffer->width = width;
+ buffer->draw_buffer = new uchar[buffer->data_size];
+ buffer->draw_buffer_needs_commit = false;
+//fprintf(stderr, "create_shm_buffer: %dx%d = %d\n", width, height, size);
+ cairo_init(buffer, width, height, stride, Fl_Wayland_Graphics_Driver::cairo_format);
+ return buffer;
+}
+
+
+void Fl_Wayland_Graphics_Driver::buffer_commit(struct wld_window *window) {
+ cairo_surface_t *surf = cairo_get_target(window->buffer->cairo_);
+ cairo_surface_flush(surf);
+ memcpy(window->buffer->data, window->buffer->draw_buffer, window->buffer->data_size);
+ wl_surface_attach(window->wl_surface, window->buffer->wl_buffer, 0, 0);
+ wl_surface_set_buffer_scale(window->wl_surface, window->scale);
+ wl_surface_commit(window->wl_surface);
+ window->buffer->draw_buffer_needs_commit = false;
+//fprintf(stderr,"buffer_commit %s\n", window->fl_win->parent()?"child":"top");
+}
+
+
+void Fl_Wayland_Graphics_Driver::cairo_init(struct fl_wld_buffer *buffer, int width, int height, int stride, cairo_format_t format) {
+ cairo_surface_t *surf = cairo_image_surface_create_for_data(buffer->draw_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_set_source_rgba(buffer->cairo_, 1.0, 1.0, 1.0, 0.);
+ cairo_paint(buffer->cairo_);
+ cairo_set_source_rgba(buffer->cairo_, .0, .0, .0, 1.0); // Black default color
+ buffer->pango_layout_ = pango_cairo_create_layout(buffer->cairo_);
+ cairo_save(buffer->cairo_);
+}
+
+
+void Fl_Wayland_Graphics_Driver::buffer_release(struct wld_window *window)
+{
+ if (window->buffer) {
+ wl_buffer_destroy(window->buffer->wl_buffer);
+ delete[] window->buffer->draw_buffer;
+ window->buffer->draw_buffer = NULL;
+ cairo_surface_t *surf = cairo_get_target(window->buffer->cairo_);
+ cairo_destroy(window->buffer->cairo_);
+ cairo_surface_destroy(surf);
+ g_object_unref(window->buffer->pango_layout_);
+ free(window->buffer);
+ window->buffer = NULL;
+ }
+}
+
+// these 2 refer to the same memory layout for pixel data
+const uint32_t Fl_Wayland_Graphics_Driver::wld_format = WL_SHM_FORMAT_ARGB8888;
+const cairo_format_t Fl_Wayland_Graphics_Driver::cairo_format = CAIRO_FORMAT_ARGB32;
+
+
+Fl_Wayland_Graphics_Driver::Fl_Wayland_Graphics_Driver () : Fl_Cairo_Graphics_Driver() {
+ dummy_pango_layout_ = NULL;
+ linestyle_ = 0;
+}
+
+
+Fl_Graphics_Driver *Fl_Graphics_Driver::newMainGraphicsDriver()
+{
+ fl_graphics_driver = new Fl_Wayland_Graphics_Driver();
+ return fl_graphics_driver;
+}
+
+
+Fl_Wayland_Graphics_Driver::~Fl_Wayland_Graphics_Driver() {
+ if (pango_layout_) g_object_unref(pango_layout_);
+}
+
+
+void Fl_Wayland_Graphics_Driver::activate(struct fl_wld_buffer *buffer, float scale) {
+ if (dummy_pango_layout_) {
+ cairo_surface_t *surf = cairo_get_target(cairo_);
+ cairo_destroy(cairo_);
+ cairo_surface_destroy(surf);
+ g_object_unref(dummy_pango_layout_);
+ dummy_pango_layout_ = NULL;
+ pango_layout_ = NULL;
+ }
+ cairo_ = buffer->cairo_;
+ if (pango_layout_ != buffer->pango_layout_) {
+ if (pango_layout_) g_object_unref(pango_layout_);
+ pango_layout_ = buffer->pango_layout_;
+ g_object_ref(pango_layout_);
+ Fl_Graphics_Driver::font(-1, -1); // signal that no font is current yet
+ }
+ this->buffer_ = buffer;
+ cairo_restore(cairo_);
+ cairo_save(cairo_);
+ cairo_scale(cairo_, scale, scale);
+ cairo_translate(cairo_, 0.5, 0.5);
+ line_style(0);
+}
+
+
+static Fl_Fontdesc built_in_table[] = { // Pango font names
+ {"Sans"},
+ {"Sans Bold"},
+ {"Sans Italic"},
+ {"Sans Bold Italic"},
+ {"Monospace"},
+ {"Monospace Bold"},
+ {"Monospace Italic"},
+ {"Monospace Bold Italic"},
+ {"Serif"},
+ {"Serif Bold"},
+ {"Serif Italic"},
+ {"Serif Bold Italic"},
+ {"Standard Symbols PS"}, // FL_SYMBOL
+ {"Monospace"}, // FL_SCREEN
+ {"Monospace Bold"}, // FL_SCREEN_BOLD
+ {"D050000L"}, // FL_ZAPF_DINGBATS
+};
+
+FL_EXPORT Fl_Fontdesc *fl_fonts = built_in_table;
+
+
+static Fl_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size) {
+ Fl_Fontdesc* s = fl_fonts+fnum;
+ if (!s->name) s = fl_fonts; // use 0 if fnum undefined
+ Fl_Font_Descriptor* f;
+ for (f = s->first; f; f = f->next)
+ if (f->size == size) return f;
+ f = new Fl_Wayland_Font_Descriptor(s->name, size);
+ f->next = s->first;
+ s->first = f;
+ return f;
+}
+
+
+Fl_Wayland_Font_Descriptor::Fl_Wayland_Font_Descriptor(const char* name, Fl_Fontsize size) : Fl_Font_Descriptor(name, size) {
+ char string[70];
+ strcpy(string, name);
+ sprintf(string + strlen(string), " %d", int(size * 0.7 + 0.5) ); // why reduce size?
+ fontref = pango_font_description_from_string(string);
+ width = NULL;
+ static PangoFontMap *def_font_map = pango_cairo_font_map_get_default(); // 1.10
+ static PangoContext *pango_context = pango_font_map_create_context(def_font_map); // 1.22
+ static PangoLanguage *language = pango_language_get_default(); // 1.16
+ PangoFontset *fontset = pango_font_map_load_fontset(def_font_map, pango_context, fontref, language);
+ PangoFontMetrics *metrics = pango_fontset_get_metrics(fontset);
+ ascent = pango_font_metrics_get_ascent(metrics)/PANGO_SCALE;
+ descent = pango_font_metrics_get_descent(metrics)/PANGO_SCALE;
+ q_width = pango_font_metrics_get_approximate_char_width(metrics)/PANGO_SCALE;
+ pango_font_metrics_unref(metrics);
+ g_object_unref(fontset);
+//fprintf(stderr, "[%s](%d) ascent=%d descent=%d q_width=%d\n", name, size, ascent, descent, q_width);
+}
+
+
+Fl_Wayland_Font_Descriptor::~Fl_Wayland_Font_Descriptor() {
+ pango_font_description_free(fontref);
+ if (width) {
+ for (int i = 0; i < 64; i++) delete[] width[i];
+ }
+ delete[] width;
+}
+
+
+int Fl_Wayland_Graphics_Driver::height() {
+ return (font_descriptor()->ascent + font_descriptor()->descent)*1.1 /*1.15 scale=1*/;
+}
+
+
+int Fl_Wayland_Graphics_Driver::descent() {
+ return font_descriptor()->descent;
+}
+
+
+void Fl_Wayland_Graphics_Driver::font(Fl_Font fnum, Fl_Fontsize s) {
+ if (font() == fnum && size() == s) return;
+ if (!font_descriptor()) fl_open_display();
+ if (!pango_layout_) {
+ cairo_surface_t *surf = cairo_image_surface_create(Fl_Wayland_Graphics_Driver::cairo_format, 100, 100);
+ cairo_ = cairo_create(surf);
+ dummy_pango_layout_ = pango_cairo_create_layout(cairo_);
+ pango_layout_ = dummy_pango_layout_;
+ }
+ if (fnum == -1) {
+ Fl_Graphics_Driver::font(0, 0);
+ return;
+ }
+ Fl_Graphics_Driver::font(fnum, s);
+ font_descriptor( find(fnum, s) );
+ pango_layout_set_font_description(pango_layout_, ((Fl_Wayland_Font_Descriptor*)font_descriptor())->fontref);
+}
+
+
+static int font_name_process(const char *name, char &face) {
+ int l = strlen(name);
+ face = ' ';
+ if (!memcmp(name + l - 8, " Regular", 8)) l -= 8;
+ else if (!memcmp(name + l - 6, " Plain", 6)) l -= 6;
+ else if (!memcmp(name + l - 12, " Bold Italic", 12)) {l -= 12; face='P';}
+ else if (!memcmp(name + l - 7, " Italic", 7)) {l -= 7; face='I';}
+ else if (!memcmp(name + l - 5, " Bold", 5)) {l -= 5; face='B';}
+ return l;
+}
+
+typedef int (*sort_f_type)(const void *aa, const void *bb);
+
+
+static int font_sort(Fl_Fontdesc *fa, Fl_Fontdesc *fb) {
+ char face_a, face_b;
+ int la = font_name_process(fa->name, face_a);
+ int lb = font_name_process(fb->name, face_b);
+ int c = strncasecmp(fa->name, fb->name, la >= lb ? lb : la);
+ return (c == 0 ? face_a - face_b : c);
+}
+
+
+Fl_Font Fl_Wayland_Graphics_Driver::set_fonts(const char* pattern_name)
+{
+ fl_open_display();
+ int n_families, count = 0;
+ PangoFontFamily **families;
+ static PangoFontMap *pfmap_ = pango_cairo_font_map_get_default(); // 1.10
+ Fl_Wayland_Graphics_Driver::init_built_in_fonts();
+ pango_font_map_list_families(pfmap_, &families, &n_families);
+ for (int fam = 0; fam < n_families; fam++) {
+ PangoFontFace **faces;
+ int n_faces;
+ const char *fam_name = pango_font_family_get_name (families[fam]);
+ int l = strlen(fam_name);
+ pango_font_family_list_faces(families[fam], &faces, &n_faces);
+ for (int j = 0; j < n_faces; j++) {
+ const char *p = pango_font_face_get_face_name(faces[j]);
+ // build the font's FLTK name
+ l += strlen(p) + 2;
+ char *q = new char[l];
+ sprintf(q, "%s %s", fam_name, p);
+ Fl::set_font((Fl_Font)(count++ + FL_FREE_FONT), q);
+ }
+ /*g_*/free(faces); // glib source code shows that g_free is equivalent to free
+ }
+ /*g_*/free(families);
+ // Sort the list into alphabetic order
+ qsort(fl_fonts + FL_FREE_FONT, count, sizeof(Fl_Fontdesc), (sort_f_type)font_sort);
+ return FL_FREE_FONT + count;
+}
+
+
+void Fl_Wayland_Graphics_Driver::init_built_in_fonts() {
+ static int i = 0;
+ if (!i) {
+ while (i < FL_FREE_FONT) {
+ i++;
+ Fl::set_font((Fl_Font)i-1, built_in_table[i-1].name);
+ }
+ }
+}
+
+
+const char *Fl_Wayland_Graphics_Driver::font_name(int num) {
+ return fl_fonts[num].name;
+}
+
+
+void Fl_Wayland_Graphics_Driver::font_name(int num, const char *name) {
+ Fl_Fontdesc *s = fl_fonts + num;
+ if (s->name) {
+ if (!strcmp(s->name, name)) {s->name = name; return;}
+ for (Fl_Font_Descriptor* f = s->first; f;) {
+ Fl_Font_Descriptor* n = f->next; delete f; f = n;
+ }
+ s->first = 0;
+ }
+ s->name = name;
+ s->fontname[0] = 0;
+ s->first = 0;
+}
+
+#define ENDOFBUFFER sizeof(fl_fonts->fontname)-1
+
+// turn a stored font name into a pretty name:
+const char* Fl_Wayland_Graphics_Driver::get_font_name(Fl_Font fnum, int* ap) {
+ Fl_Fontdesc *f = fl_fonts + fnum;
+ if (!f->fontname[0]) {
+ strcpy(f->fontname, f->name); // to check
+ const char* thisFont = f->name;
+ if (!thisFont || !*thisFont) {if (ap) *ap = 0; return "";}
+ int type = 0;
+ if (strstr(f->name, "Bold")) type |= FL_BOLD;
+ if (strstr(f->name, "Italic") || strstr(f->name, "Oblique")) type |= FL_ITALIC;
+ f->fontname[ENDOFBUFFER] = (char)type;
+ }
+ if (ap) *ap = f->fontname[ENDOFBUFFER];
+ return f->fontname;
+}
+
+
+int Fl_Wayland_Graphics_Driver::get_font_sizes(Fl_Font fnum, int*& sizep) {
+ static int array[128];
+ if (!fl_fonts) fl_fonts = calc_fl_fonts();
+ Fl_Fontdesc *s = fl_fonts+fnum;
+ if (!s->name) s = fl_fonts; // empty slot in table, use entry 0
+ int cnt = 0;
+
+ array[0] = 0;
+ sizep = array;
+ cnt = 1;
+
+ return cnt;
+}
+
+
+void Fl_Wayland_Graphics_Driver::draw(const char* str, int n, float x, float y) {
+ if (!n) return;
+ cairo_save(cairo_);
+ cairo_translate(cairo_, x, y - height() + descent() -1);
+ pango_layout_set_text(pango_layout_, str, n);
+ pango_cairo_show_layout(cairo_, pango_layout_);
+ cairo_restore(cairo_);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+
+void Fl_Wayland_Graphics_Driver::draw(int rotation, const char *str, int n, int x, int y)
+{
+ cairo_save(cairo_);
+ cairo_translate(cairo_, x, y);
+ cairo_rotate(cairo_, -rotation * M_PI / 180);
+ this->draw(str, n, 0, 0);
+ cairo_restore(cairo_);
+}
+
+
+void Fl_Wayland_Graphics_Driver::rtl_draw(const char* str, int n, int x, int y) {
+ int w = (int)width(str, n);
+ draw(str, n, x - w, y);
+}
+
+
+double Fl_Wayland_Graphics_Driver::width(const char* c, int n) {
+ if (!font_descriptor()) return -1.0;
+ int i = 0, w = 0, l;
+ const char *end = c + n;
+ unsigned int ucs;
+ while (i < n) {
+ ucs = fl_utf8decode(c + i, end, &l);
+ i += l;
+ w += width(ucs);
+ }
+ return (double)w;
+}
+
+
+double Fl_Wayland_Graphics_Driver::width(unsigned int c) {
+ unsigned int r = 0;
+ Fl_Wayland_Font_Descriptor *desc = NULL;
+ if (c <= 0xFFFF) { // when inside basic multilingual plane
+ desc = (Fl_Wayland_Font_Descriptor*)font_descriptor();
+ r = (c & 0xFC00) >> 10;
+ if (!desc->width) {
+ desc->width = (int**)new int*[64];
+ memset(desc->width, 0, 64*sizeof(int*));
+ }
+ if (!desc->width[r]) {
+ desc->width[r] = (int*)new int[0x0400];
+ for (int i = 0; i < 0x0400; i++) desc->width[r][i] = -1;
+ } else {
+ if ( desc->width[r][c & 0x03FF] >= 0 ) { // already cached
+ return (double) desc->width[r][c & 0x03FF];
+ }
+ }
+ }
+ char buf[4];
+ int n = fl_utf8encode(c, buf);
+ pango_layout_set_text(pango_layout_, buf, n);
+ int W = 0, H;
+ pango_layout_get_pixel_size(pango_layout_, &W, &H);
+ if (c <= 0xFFFF) desc->width[r][c & 0x03FF] = W;
+ return (double)W;
+}
+
+
+void Fl_Wayland_Graphics_Driver::text_extents(const char* txt, int n, int& dx, int& dy, int& w, int& h) {
+ pango_layout_set_text(pango_layout_, txt, n);
+ PangoRectangle ink_rect;
+ pango_layout_get_pixel_extents(pango_layout_, &ink_rect, NULL);
+ dx = ink_rect.x;
+ dy = ink_rect.y - height() + descent();
+ w = ink_rect.width;
+ h = ink_rect.height;
+}
+
+
+int Fl_Wayland_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
+ if (!clip_) return 1;
+ if (clip_->w < 0) return 1;
+ int X = 0, Y = 0, W = 0, H = 0;
+ clip_box(x, y, w, h, X, Y, W, H);
+ if (W) return 1;
+ return 0;
+}
+
+int Fl_Wayland_Graphics_Driver::clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) {
+ if (!clip_) {
+ X = x; Y = y; W = w; H = h;
+ return 0;
+ }
+ if (clip_->w < 0) {
+ X = x; Y = y; W = w; H = h;
+ return 1;
+ }
+ int ret = 0;
+ if (x > (X=clip_->x)) {X=x; ret=1;}
+ if (y > (Y=clip_->y)) {Y=y; ret=1;}
+ if ((x+w) < (clip_->x+clip_->w)) {
+ W=x+w-X;
+
+ ret=1;
+
+ }else
+ W = clip_->x + clip_->w - X;
+ if(W<0){
+ W=0;
+ return 1;
+ }
+ if ((y+h) < (clip_->y+clip_->h)) {
+ H=y+h-Y;
+ ret=1;
+ }else
+ H = clip_->y + clip_->h - Y;
+ if(H<0){
+ W=0;
+ H=0;
+ return 1;
+ }
+ return ret;
+}
+
+void Fl_Wayland_Graphics_Driver::restore_clip() {
+ if (cairo_) cairo_reset_clip(cairo_);
+}
+
+void Fl_Wayland_Graphics_Driver::clip_region(Fl_Region r) {
+ if (cairo_) {
+ cairo_reset_clip(cairo_);
+ if (r) {
+ for (int i = 0; i < r->count; i++) {
+ cairo_rectangle(cairo_, r->rects[i].x-0.5 , r->rects[i].y-0.5 , r->rects[i].width , r->rects[i].height);
+ }
+ cairo_clip(cairo_);
+ }
+ }
+}
+
+
+Fl_Region Fl_Wayland_Graphics_Driver::XRectangleRegion(int x, int y, int w, int h) {
+ Fl_Region R = (Fl_Region)malloc(sizeof(*R));
+ R->count = 1;
+ R->rects = (cairo_rectangle_t *)malloc(sizeof(cairo_rectangle_t));
+ R->rects->x=x, R->rects->y=y, R->rects->width=w; R->rects->height=h;
+ return R;
+}
+
+
+// r1 ⊂ r2
+static bool CairoRectContainsRect(cairo_rectangle_t *r1, cairo_rectangle_t *r2) {
+ return r1->x >= r2->x && r1->y >= r2->y && r1->x+r1->width <= r2->x+r2->width &&
+ r1->y+r1->height <= r2->y+r2->height;
+}
+
+
+void Fl_Wayland_Graphics_Driver::add_rectangle_to_region(Fl_Region r, int X, int Y, int W, int H) {
+ cairo_rectangle_t arg = {double(X), double(Y), double(W), double(H)};
+ int j; // don't add a rectangle totally inside the Fl_Region
+ for (j = 0; j < r->count; j++) {
+ if (CairoRectContainsRect(&arg, &(r->rects[j]))) break;
+ }
+ if (j >= r->count) {
+ r->rects = (cairo_rectangle_t*)realloc(r->rects, (++(r->count)) * sizeof(cairo_rectangle_t));
+ r->rects[r->count - 1] = arg;
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::XDestroyRegion(Fl_Region r) {
+ if (r) {
+ free(r->rects);
+ free(r);
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::set_color(Fl_Color i, unsigned c) {
+ if (fl_cmap[i] != c) {
+ fl_cmap[i] = c;
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::point(int x, int y) {
+ rectf(x, y, 1, 1);
+}
+
+
+void Fl_Wayland_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen osrc, int srcx, int srcy) {
+ // draw portion srcx,srcy,w,h of osrc to position x,y (top-left) of the graphics driver's surface
+ int height = osrc->data_size / osrc->stride;
+ cairo_matrix_t matrix;
+ cairo_get_matrix(cairo_, &matrix);
+ double s = matrix.xx;
+ cairo_save(cairo_);
+ cairo_rectangle(cairo_, x, y, w, h);
+ cairo_clip(cairo_);
+ cairo_surface_t *surf = cairo_image_surface_create_for_data(osrc->draw_buffer, Fl_Wayland_Graphics_Driver::cairo_format, osrc->width, height, osrc->stride);
+ 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_mask(cairo_, pat);
+ cairo_pattern_destroy(pat);
+ cairo_surface_destroy(surf);
+ cairo_restore(cairo_);
+}
+
+
+struct callback_data {
+ const uchar *data;
+ int D, LD;
+};
+
+
+static void draw_image_cb(void *data, int x, int y, int w, uchar *buf) {
+ struct callback_data *cb_data;
+ const uchar *curdata;
+
+ cb_data = (struct callback_data*)data;
+ int last = x+w;
+ const size_t aD = abs(cb_data->D);
+ curdata = cb_data->data + x*cb_data->D + y*cb_data->LD;
+ for (; x<last; x++) {
+ memcpy(buf, curdata, aD);
+ buf += aD;
+ curdata += cb_data->D;
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::draw_image(const uchar *data, int ix, int iy, int iw, int ih, int D, int LD) {
+ if (abs(D)<3){ //mono
+ draw_image_mono(data, ix, iy, iw, ih, D, LD);
+ return;
+ }
+ struct callback_data cb_data;
+ if (!LD) LD = iw*abs(D);
+ if (D<0) data += iw*abs(D);
+ cb_data.data = data;
+ cb_data.D = D;
+ cb_data.LD = LD;
+ Fl_Cairo_Graphics_Driver::draw_image(draw_image_cb, &cb_data, ix, iy, iw, ih, abs(D));
+}
+
+
+void Fl_Wayland_Graphics_Driver::curve(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3) {
+ if (shape_ == POINTS) Fl_Graphics_Driver::curve(x, y, x1, y1, x2, y2, x3, y3);
+ else Fl_Cairo_Graphics_Driver::curve(x, y, x1, y1, x2, y2, x3, y3);
+}
+
+
+void Fl_Wayland_Graphics_Driver::begin_points() {
+ cairo_save(cairo_);
+ gap_=1;
+ shape_=POINTS;
+}
+
+
+void Fl_Wayland_Graphics_Driver::end_points() {
+ cairo_restore(cairo_);
+}
+
+
+void Fl_Wayland_Graphics_Driver::transformed_vertex(double x, double y) {
+ if (shape_ == POINTS){
+ cairo_move_to(cairo_, x, y);
+ point(x, y);
+ gap_ = 1;
+ } else {
+ Fl_Cairo_Graphics_Driver::transformed_vertex(x, y);
+ }
+}
+
+void Fl_Wayland_Graphics_Driver::line_style(int style, int width, char* dashes) {
+ linestyle_ = style;
+ Fl_Cairo_Graphics_Driver::line_style(style, width, dashes);
+}
+
+void Fl_Wayland_Graphics_Driver::overlay_rect(int x, int y, int w , int h) {
+ cairo_save(cairo_);
+ cairo_matrix_t mat;
+ cairo_get_matrix(cairo_, &mat);
+ float s = (float)mat.xx;
+ cairo_matrix_init_identity(&mat);
+ cairo_set_matrix(cairo_, &mat); // use drawing units
+ int lwidth = s < 1 ? 1 : int(s);
+ cairo_set_line_width(cairo_, lwidth);
+ cairo_translate(cairo_, lwidth/2., lwidth/2.); // translate by half of line width
+ double ddash = (lwidth > 2 ? lwidth : 2);
+ if (linestyle_ == FL_DOT){
+ cairo_set_dash(cairo_, &ddash, 1, 0); // dash size = line width
+ }
+ // rectangle in drawing units
+ int Xs = Fl_Scalable_Graphics_Driver::floor(x, s);
+ int Ws = Fl_Scalable_Graphics_Driver::floor(x+w-1, s) - Xs;
+ int Ys = Fl_Scalable_Graphics_Driver::floor(y, s);
+ int Hs = Fl_Scalable_Graphics_Driver::floor(y+h-1, s) - Ys;
+ cairo_move_to(cairo_, Xs, Ys);
+ cairo_line_to(cairo_, Xs+Ws, Ys);
+ cairo_line_to(cairo_, Xs+Ws, Ys+Hs);
+ cairo_line_to(cairo_, Xs, Ys+Hs);
+ cairo_close_path(cairo_);
+ cairo_stroke(cairo_);
+ cairo_restore(cairo_);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+
+void Fl_Wayland_Graphics_Driver::draw_cached_pattern_(Fl_Image *img, cairo_pattern_t *pat, int X, int Y, int W, int H, int cx, int cy) {
+ // compute size of output image in drawing units
+ cairo_matrix_t matrix;
+ cairo_get_matrix(cairo_, &matrix);
+ float s = (float)matrix.xx;
+ int Xs = Fl_Scalable_Graphics_Driver::floor(X - cx, s);
+ int Ws = Fl_Scalable_Graphics_Driver::floor(X - cx + img->w(), s) - Xs ;
+ int Ys = Fl_Scalable_Graphics_Driver::floor(Y - cy, s);
+ int Hs = Fl_Scalable_Graphics_Driver::floor(Y - cy + img->h(), s) - Ys;
+ if (Ws == 0 || Hs == 0) return;
+ cairo_save(cairo_);
+ if (cx || cy || W < img->w() || H < img->h()) { // clip when necessary
+ cairo_rectangle(cairo_, X-0.5, Y-0.5, W+1, H+1);
+ cairo_clip(cairo_);
+ }
+ // remove any scaling and the current "0.5" translation useful for lines but bad for images
+ matrix.xx = matrix.yy = 1;
+ matrix.x0 -= 0.5 * s; matrix.y0 -= 0.5 * s;
+ cairo_set_matrix(cairo_, &matrix);
+ if (img->d() >= 1) cairo_set_source(cairo_, pat);
+ int offset = 0;
+ if (Ws >= img->data_w()*1.09 || Hs >= img->data_h()*1.09) {
+ // When enlarging while drawing, 1 pixel around target area seems unpainted,
+ // so we increase a bit the target area and move it int(s) pixels to left and top.
+ Ws = (img->w()+2)*s, Hs = (img->h()+2)*s;
+ offset = int(s);
+ }
+
+//fprintf(stderr,"WHs=%dx%d dataWH=%dx%d s=%.1f offset=%d\n",Ws,Hs,img->data_w(),img->data_h(),s,offset);
+ cairo_matrix_init_scale(&matrix, double(img->data_w())/Ws, double(img->data_h())/Hs);
+ cairo_matrix_translate(&matrix, -Xs + offset, -Ys + offset);
+ cairo_pattern_set_matrix(pat, &matrix);
+ cairo_mask(cairo_, pat);
+ cairo_restore(cairo_);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+
+void Fl_Wayland_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb,int XP, int YP, int WP, int HP, int cx, int cy) {
+ int X, Y, W, H;
+ // Don't draw an empty image...
+ if (!rgb->d() || !rgb->array) {
+ Fl_Graphics_Driver::draw_empty(rgb, XP, YP);
+ return;
+ }
+ if (start_image(rgb, XP, YP, WP, HP, cx, cy, X, Y, W, H)) {
+ return;
+ }
+ cairo_pattern_t *pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(rgb);
+ if (!pat) {
+ cache(rgb);
+ pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(rgb);
+ }
+ draw_cached_pattern_(rgb, pat, X, Y, W, H, cx, cy);
+}
+
+
+static cairo_user_data_key_t data_key_for_surface = {};
+
+static void dealloc_surface_data(void *data) {
+ delete[] (uchar*)data;
+}
+
+
+void Fl_Wayland_Graphics_Driver::cache(Fl_RGB_Image *rgb) {
+ int stride = cairo_format_stride_for_width(Fl_Wayland_Graphics_Driver::cairo_format, rgb->data_w());
+ uchar *BGRA = new uchar[stride * rgb->data_h()];
+ memset(BGRA, 0, stride * rgb->data_h());
+ int lrgb = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d();
+ uchar A = 0xff, R,G,B, *q;
+ const uchar *r;
+ float f = 1;
+ if (rgb->d() >= 3) { // color images
+ for (int j = 0; j < rgb->data_h(); j++) {
+ r = rgb->array + j * lrgb;
+ q = BGRA + j * stride;
+ for (int i = 0; i < rgb->data_w(); i++) {
+ R = *r;
+ G = *(r+1);
+ B = *(r+2);
+ if (rgb->d() == 4) {
+ A = *(r+3);
+ f = float(A)/0xff;
+ }
+ *q = B * f;
+ *(q+1) = G * f;
+ *(q+2) = R * f;
+ *(q+3) = A;
+ r += rgb->d(); q += 4;
+ }
+ }
+ } else if (rgb->d() == 1 || rgb->d() == 2) { // B&W
+ for (int j = 0; j < rgb->data_h(); j++) {
+ r = rgb->array + j * lrgb;
+ q = BGRA + j * stride;
+ for (int i = 0; i < rgb->data_w(); i++) {
+ G = *r;
+ if (rgb->d() == 2) {
+ A = *(r+1);
+ f = float(A)/0xff;
+ }
+ *(q) = G * f;
+ *(q+1) = G * f;
+ *(q+2) = G * f;
+ *(q+3) = A;
+ r += rgb->d(); q += 4;
+ }
+ }
+ }
+ cairo_surface_t *surf = cairo_image_surface_create_for_data(BGRA, Fl_Wayland_Graphics_Driver::cairo_format, rgb->data_w(), rgb->data_h(), stride);
+ if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) return;
+ (void)cairo_surface_set_user_data(surf, &data_key_for_surface, BGRA, dealloc_surface_data);
+ cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf);
+ *Fl_Graphics_Driver::id(rgb) = (fl_uintptr_t)pat;
+}
+
+
+void Fl_Wayland_Graphics_Driver::uncache(Fl_RGB_Image *img, fl_uintptr_t &id_, fl_uintptr_t &mask_) {
+ cairo_pattern_t *pat = (cairo_pattern_t*)id_;
+ if (pat) {
+ cairo_surface_t *surf;
+ cairo_pattern_get_surface(pat, &surf);
+ cairo_pattern_destroy(pat);
+ cairo_surface_destroy(surf);
+ id_ = 0;
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::draw_bitmap(Fl_Bitmap *bm,int XP, int YP, int WP, int HP, int cx, int cy) {
+ int X, Y, W, H;
+ if (!bm->array) {
+ draw_empty(bm, XP, YP);
+ return;
+ }
+ if (start_image(bm, XP,YP,WP,HP,cx,cy,X,Y,W,H)) return;
+ cairo_pattern_t *pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(bm);
+ if (!pat) {
+ cache(bm);
+ pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(bm);
+ }
+ if (pat) {
+ draw_cached_pattern_(bm, pat, X, Y, W, H, cx, cy);
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::cache(Fl_Bitmap *bm) {
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_A1, bm->data_w());
+ uchar *BGRA = new uchar[stride * bm->data_h()];
+ memset(BGRA, 0, stride * bm->data_h());
+ uchar *r, p;
+ unsigned *q;
+ for (int j = 0; j < bm->data_h(); j++) {
+ r = (uchar*)bm->array + j * ((bm->data_w() + 7)/8);
+ q = (unsigned*)(BGRA + j * stride);
+ unsigned k = 0, mask32 = 1;
+ p = *r;
+ for (int i = 0; i < bm->data_w(); i++) {
+ if (p&1) (*q) |= mask32;
+ k++;
+ if (k % 8 != 0) p >>= 1; else p = *(++r);
+ if (k % 32 != 0) mask32 <<= 1; else {q++; mask32 = 1;}
+ }
+ }
+ cairo_surface_t *surf = cairo_image_surface_create_for_data(BGRA, CAIRO_FORMAT_A1, bm->data_w(), bm->data_h(), stride);
+ if (cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS) {
+ (void)cairo_surface_set_user_data(surf, &data_key_for_surface, BGRA, dealloc_surface_data);
+ cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf);
+ *Fl_Graphics_Driver::id(bm) = (fl_uintptr_t)pat;
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::delete_bitmask(Fl_Bitmask bm) {
+ cairo_pattern_t *pat = (cairo_pattern_t*)bm;
+ if (pat) {
+ cairo_surface_t *surf;
+ cairo_pattern_get_surface(pat, &surf);
+ cairo_pattern_destroy(pat);
+ cairo_surface_destroy(surf);
+ }
+}
+
+
+void Fl_Wayland_Graphics_Driver::draw_pixmap(Fl_Pixmap *pxm,int XP, int YP, int WP, int HP, int cx, int cy) {
+ int X, Y, W, H;
+ // Don't draw an empty image...
+ if (!pxm->data() || !pxm->w()) {
+ Fl_Graphics_Driver::draw_empty(pxm, XP, YP);
+ return;
+ }
+ if (start_image(pxm, XP, YP, WP, HP, cx, cy, X, Y, W, H)) {
+ return;
+ }
+ cairo_pattern_t *pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(pxm);
+ if (!pat) {
+ cache(pxm);
+ pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(pxm);
+ }
+ draw_cached_pattern_(pxm, pat, X, Y, W, H, cx, cy);
+}
+
+
+void Fl_Wayland_Graphics_Driver::cache(Fl_Pixmap *pxm) {
+ Fl_RGB_Image *rgb = new Fl_RGB_Image(pxm);
+ cache(rgb);
+ *Fl_Graphics_Driver::id(pxm) = *Fl_Graphics_Driver::id(rgb);
+ *Fl_Graphics_Driver::id(rgb) = 0;
+ delete rgb;
+}
+
+
+void Fl_Wayland_Graphics_Driver::uncache_pixmap(fl_uintptr_t p) {
+ cairo_pattern_t *pat = (cairo_pattern_t*)p;
+ if (pat) {
+ cairo_surface_t *surf;
+ cairo_pattern_get_surface(pat, &surf);
+ cairo_pattern_destroy(pat);
+ cairo_surface_destroy(surf);
+ }
+}
+
+
+void Fl_Wayland_Graphics_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_Graphics_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_Graphics_Driver::line(int x1, int y1, int x2, int y2) {
+ Fl_Cairo_Graphics_Driver::line(x1, y1, x2, y2);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::line(int x1, int y1, int x2, int y2, int x3, int y3) {
+ Fl_Cairo_Graphics_Driver::line(x1, y1, x2, y2, x3, y3);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::xyline(int x, int y, int x1) {
+ Fl_Cairo_Graphics_Driver::xyline(x, y, x1);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::xyline(int x, int y, int x1, int y2) {
+ Fl_Cairo_Graphics_Driver::xyline(x, y, x1, y2);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3) {
+ Fl_Cairo_Graphics_Driver::xyline(x, y, x1, y2, x3);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::yxline(int x, int y, int y1) {
+ Fl_Cairo_Graphics_Driver::yxline(x, y, y1);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::yxline(int x, int y, int y1, int x2) {
+ Fl_Cairo_Graphics_Driver::yxline(x, y, y1, x2);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::yxline(int x, int y, int y1, int x2, int y3) {
+ Fl_Cairo_Graphics_Driver::yxline(x, y, y1, x2, y3);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) {
+ Fl_Cairo_Graphics_Driver::loop(x0, y0, x1, y1, x2, y2);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
+ Fl_Cairo_Graphics_Driver::loop(x0, y0, x1, y1, x2, y2, x3, y3);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::rectf(int x, int y, int w, int h) {
+ Fl_Cairo_Graphics_Driver::rectf(x, y, w, h);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::rect(int x, int y, int w, int h) {
+ Fl_Cairo_Graphics_Driver::rect(x, y, w, h);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) {
+ Fl_Cairo_Graphics_Driver::polygon(x0, y0, x1, y1, x2, y2);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
+ Fl_Cairo_Graphics_Driver::polygon(x0, y0, x1, y1, x2, y2, x3, y3);
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::end_line() {
+ Fl_Cairo_Graphics_Driver::end_line();
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::end_loop(){
+ Fl_Cairo_Graphics_Driver::end_loop();
+ buffer_->draw_buffer_needs_commit = true;
+}
+
+void Fl_Wayland_Graphics_Driver::end_polygon() {
+ Fl_Cairo_Graphics_Driver::end_polygon();
+ buffer_->draw_buffer_needs_commit = true;
+}
diff --git a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx
new file mode 100644
index 000000000..d79ad0f2e
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx
@@ -0,0 +1,107 @@
+//
+// Draw-to-image code for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2021 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#include <FL/platform.H>
+#include "Fl_Wayland_Graphics_Driver.H"
+#include "Fl_Wayland_Window_Driver.H"
+#include <FL/Fl_Image_Surface.H>
+
+class Fl_Wayland_Image_Surface_Driver : public Fl_Image_Surface_Driver {
+ virtual void end_current();
+public:
+ Fl_Wayland_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off);
+ ~Fl_Wayland_Image_Surface_Driver();
+ void set_current();
+ void translate(int x, int y);
+ void untranslate();
+ Fl_RGB_Image *image();
+};
+
+Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, int h, int high_res, Fl_Offscreen off)
+{
+ return new Fl_Wayland_Image_Surface_Driver(w, h, high_res, off);
+}
+
+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) {
+ float d = 1;
+ if (!off) {
+ fl_open_display();
+ if (fl_window) {
+ d = fl_window->scale;
+ }
+ d *= fl_graphics_driver->scale();
+ if (d != 1 && high_res) {
+ w = int(w*d);
+ h = int(h*d);
+ }
+ offscreen = (struct fl_wld_buffer*)calloc(1, sizeof(struct fl_wld_buffer));
+ offscreen->stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
+ offscreen->data_size = offscreen->stride * h;
+ offscreen->draw_buffer = (uchar*)malloc(offscreen->data_size);
+ offscreen->width = w;
+ Fl_Wayland_Graphics_Driver::cairo_init(offscreen, w, h, offscreen->stride, CAIRO_FORMAT_RGB24);
+ }
+ driver(new Fl_Wayland_Graphics_Driver());
+ if (d != 1 && high_res) driver()->scale(d);
+}
+
+
+Fl_Wayland_Image_Surface_Driver::~Fl_Wayland_Image_Surface_Driver() {
+ if (offscreen && !external_offscreen) {
+ free(offscreen->draw_buffer);
+ free(offscreen);
+ }
+ delete driver();
+}
+
+void Fl_Wayland_Image_Surface_Driver::set_current() {
+ Fl_Surface_Device::set_current();
+ ((Fl_Wayland_Graphics_Driver*)fl_graphics_driver)->activate(offscreen, driver()->scale());
+}
+
+void Fl_Wayland_Image_Surface_Driver::end_current() {
+ cairo_surface_t *surf = cairo_get_target(offscreen->cairo_);
+ cairo_surface_flush(surf);
+}
+
+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() {
+ // Convert depth-4 image in draw_buffer to a depth-3 image while exchanging R and B colors
+ int height = offscreen->data_size / offscreen->stride;
+ uchar *rgb = new uchar[offscreen->width * height * 3];
+ uchar *p = rgb;
+ uchar *q;
+ for (int j = 0; j < height; j++) {
+ q = offscreen->draw_buffer + j*offscreen->stride;
+ for (int i = 0; i < offscreen->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, offscreen->width, height, 3);
+ image->alloc_array = 1;
+ return image;
+}
diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H
new file mode 100644
index 000000000..ce10884c5
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H
@@ -0,0 +1,175 @@
+//
+// Definition of X11 Screen interface
+// for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2010-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
+//
+
+/**
+ \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 "../../Fl_Screen_Driver.H"
+#include <wayland-client.h>
+
+class Fl_Window;
+
+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 link;
+ 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;
+ 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;
+};
+
+class FL_EXPORT Fl_Wayland_Screen_Driver : public Fl_Screen_Driver
+{
+ friend class Fl_Screen_Driver;
+ friend class Fl_Wayland_Graphics_Driver;
+ 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:
+ static void insertion_point_location(int x, int y, int height);
+ static bool insertion_point_location(int *px, int *py, int *pwidth, int *pheight);
+ int get_mouse_unscaled(int &xx, int &yy);
+ void screen_count(int count) {num_screens = count;}
+
+ void reset_cursor();
+ struct wl_cursor *xc_arrow;
+ struct wl_cursor *xc_ns;
+ struct wl_cursor *xc_wait;
+ struct wl_cursor *xc_insert;
+ struct wl_cursor *xc_hand;
+ struct wl_cursor *xc_help;
+ struct wl_cursor *xc_cross;
+ struct wl_cursor *xc_move;
+ struct wl_cursor *xc_north;
+ struct wl_cursor *xc_south;
+ struct wl_cursor *xc_west;
+ struct wl_cursor *xc_east;
+ struct wl_cursor *xc_we;
+ struct wl_cursor *xc_nesw;
+ struct wl_cursor *xc_nwse;
+ struct wl_cursor *xc_sw;
+ struct wl_cursor *xc_se;
+ struct wl_cursor *xc_ne;
+ struct wl_cursor *xc_nw;
+ static const struct wl_data_device_listener *p_data_device_listener;
+
+public:
+ struct wl_compositor *wl_compositor;
+ struct wl_subcompositor *wl_subcompositor;
+ struct wl_shm *wl_shm;
+ struct wl_list seats;
+ struct seat *seat;
+ struct wl_list outputs; // linked list of all screens in system
+ struct output { // one record for each screen
+ uint32_t id;
+ short x_org;
+ short y_org;
+ short width; // in pixels
+ short height; // in pixels
+ float dpi;
+ struct wl_output *wl_output;
+ int wld_scale; // Wayland scale factor
+ float gui_scale; // FLTK scale factor
+ struct wl_list link;
+ };
+ struct libdecor *libdecor_context;
+ struct xdg_wm_base *xdg_wm_base;
+ struct zwp_text_input_manager_v3 *text_input_base;
+
+ Fl_Wayland_Screen_Driver();
+ virtual APP_SCALING_CAPABILITY rescalable() { return PER_SCREEN_APP_SCALING; }
+ virtual float scale(int n);
+ virtual void scale(int n, float f);
+ int screen_num_unscaled(int x, int y);
+
+ void copy_image(const unsigned char* data, int W, int H);
+ // --- screen configuration
+ void init_workarea();
+ virtual void init();
+ virtual int x();
+ virtual int y();
+ virtual int w();
+ virtual int h();
+ virtual void screen_xywh(int &X, int &Y, int &W, int &H, int n);
+ virtual void screen_dpi(float &h, float &v, int n=0);
+ virtual void screen_work_area(int &X, int &Y, int &W, int &H, int n);
+ // --- audible output
+ virtual void beep(int type);
+ // --- global events
+ virtual void flush();
+ virtual void grab(Fl_Window* win);
+ // --- global colors
+ virtual void get_system_colors();
+ virtual const char *get_system_scheme();
+ virtual int dnd(int unused);
+ virtual int compose(int &del);
+ virtual void compose_reset();
+ virtual 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);
+ virtual int get_mouse(int &x, int &y);
+ virtual void open_display_platform();
+ virtual void close_display();
+ // --- compute dimensions of an Fl_Offscreen
+ virtual void offscreen_size(Fl_Offscreen o, int &width, int &height);
+ virtual int has_marked_text() const;
+ static int next_marked_length; // next length of marked text after current marked text will have been replaced
+ // --- clipboard operations
+ // this one is in Fl_wayland.cxx
+ virtual void copy(const char *stuff, int len, int clipboard, const char *type);
+ // this one is in Fl_wayland.cxx
+ virtual void paste(Fl_Widget &receiver, int clipboard, const char *type);
+ // this one is in Fl_wayland.cxx
+ virtual int clipboard_contains(const char *type);
+ // --- Wayland-special
+ void set_cursor();
+ struct wl_cursor *default_cursor();
+ void default_cursor(struct wl_cursor *cursor);
+ struct wl_cursor *cache_cursor(const char *cursor_name);
+ static Fl_Window *surface_to_window(struct wl_surface *);
+ uint32_t get_serial();
+ struct wl_seat *get_wl_seat();
+ char *get_seat_name();
+ struct xkb_keymap *get_xkb_keymap();
+ static bool own_output(struct wl_output *output);
+ typedef enum {unspecified, MUTTER, WESTON, KDE} compositor_name;
+ static compositor_name compositor; // identifies the used Wayland compositor
+};
+
+
+#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
new file mode 100644
index 000000000..71a14ca22
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
@@ -0,0 +1,1428 @@
+//
+// Implementation of Wayland Screen interface
+//
+// Copyright 1998-2022 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#include "Fl_Wayland_Screen_Driver.H"
+#include "Fl_Wayland_Window_Driver.H"
+#include "Fl_Wayland_System_Driver.H"
+#include "Fl_Wayland_Graphics_Driver.H"
+#include <wayland-cursor.h>
+#include "../../../libdecor/src/libdecor.h"
+#include "xdg-shell-client-protocol.h"
+#include "../Posix/Fl_Posix_System_Driver.H"
+#include <FL/Fl.H>
+#include <FL/platform.H>
+#include <FL/fl_ask.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Tooltip.H>
+#include <FL/filename.H>
+#include "../../print_button.h"
+#include <dlfcn.h>
+#include <sys/time.h>
+#include <linux/input.h>
+#include <stdlib.h>
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-compose.h>
+#include "text-input-client-protocol.h"
+#include <assert.h>
+#include <sys/mman.h>
+extern "C" {
+ bool libdecor_get_cursor_settings(char **theme, int *size);
+}
+
+
+#define fl_max(a,b) ((a) > (b) ? (a) : (b))
+
+struct pointer_output {
+ Fl_Wayland_Screen_Driver::output* output;
+ struct wl_list link;
+};
+
+/* Implementation note about support of 3 Wayland compositors: Mutter, Weston, KDE.
+
+- About CSD and SSD :
+ * Mutter and Weston use CSD (client-side decoration) which means that libdecor.so draws all window
+ titlebars and responds to resize, minimization and maximization events.
+ * KDE uses SSD (server-side decoration) which means the OS draws titlebars according to its own rules
+ and triggers resize, minimization and maximization events.
+
+- Function registry_handle_global() runs within fl_open_display() and sets public static variable
+ Fl_Wayland_Screen_Driver::compositor to either Fl_Wayland_Screen_Driver::MUTTER, ::WESTON, or ::KDE.
+
+- Specific operations for WESTON:
+ * When a libdecor-framed window is minimized under Weston, the frame remains on display. To avoid
+ that, function libdecor_frame_set_minimized() is modified so it turns off the frame's visibility, with
+ function libdecor_frame_set_visibility(), when the window is minimized. That's implemented in file
+ libdecor/build/fl_libdecor.c. The modified libdecor_frame_set_minimized() function, part of libdecor.so,
+ needs access to variable Fl_Wayland_Screen_Driver::compositor, part of libfltk.a. This is achieved
+ calling FLTK function fl_libdecor_using_weston() which returns whether the running compositor
+ is Weston. This Weston bug has been corrected in Weston version 10. Thus, this special processing
+ is not performed when Weston version is ≥ 10.
+
+- Synchronization between drawing to buffer and committing buffer to screen.
+ Before committing a new graphics scene for display, Wayland requires to make sure the compositor is
+ ready for commit. FLTK uses frame callbacks for that.
+ A frame callback is created when an app calls Fl_Wayland_Window_Driver::make_current()
+ directly. This directs a callback listener function, called surface_frame_done, to be called by the
+ compositor when it's ready to commit a new graphics scene. This function schedules a new frame callback
+ and commits the buffer to the display.
+ A frame callback is also created by Fl_Wayland_Window_Driver::flush() when a window redraw operation
+ is needed. FLTK processes wayland events until the compositor is ready for commit and then commits
+ the new window content.
+
+ - Support of Fl_Window::border(int) :
+ FLTK uses libdecor_frame_set_visibility() to show or hide a toplevel window's frame. This doesn't work
+ with KDE which uses Server-Side Decoration. In that case, FLTK hides and re-shows the window to toggle
+ between presence and absence of a window's frame.
+*/
+
+
+/* Implementation note about screen-related information
+
+ struct wl_output : Wayland-defined, contains info about a screen, one such record for each screen
+
+ struct Fl_Wayland_Screen_Driver::output { // FLTK defined
+ uint32_t id; // screen identification
+ short x_org;
+ short y_org;
+ short width; // screen width in pixels
+ short height; // screen height in pixels
+ float dpi;
+ struct wl_output *wl_output;
+ int wld_scale; // Wayland scale
+ float gui_scale; // user-set scale
+ struct wl_list link;
+ };
+
+ struct Fl_Wayland_Window_Driver::window_output { // FLTK defined
+ Fl_Wayland_Screen_Driver::output* output;
+ struct wl_list link;
+ }
+
+ The unique Fl_Wayland_Screen_Driver object contains a member
+ "outputs" of type struct wl_list = list of Fl_Wayland_Screen_Driver::output records
+ - this list is initialised by open-display
+ - registry_handle_global() feeds the list with 1 record for each screen
+ - registry_handle_global_remove() runs when a screen is removed. It removes
+ the output record that corresponds to that screen from the unique list of screens
+ (outputs member of the Fl_Wayland_Screen_Driver) and the list of struct output objects attached
+ to each window.
+
+ Each Fl_Wayland_Window_Driver object contains a member
+ "outputs" of type struct wl_list = list of Fl_Wayland_Window_Driver::window_output records
+ - this list is fed by surface_enter() (when a surface is mapped)
+ - these records contain:
+ window_output->output = (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output);
+ where wl_output is received from OS by surface_enter()
+ - surface_leave() removes the adequate record from the list
+ - hide() empties the list
+ - Fl_Wayland_Window_Driver::update_scale() sets the scale info of the records for a given window
+ */
+
+Fl_Wayland_Screen_Driver::compositor_name Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::unspecified;
+
+extern "C" {
+ FL_EXPORT bool fl_libdecor_using_weston(void) {
+ return Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::WESTON;
+ };
+}
+
+static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
+{
+ xdg_wm_base_pong(xdg_wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = xdg_wm_base_ping,
+};
+
+
+// these are set by Fl::args() and override any system colors: from Fl_get_system_colors.cxx
+extern const char *fl_fg;
+extern const char *fl_bg;
+extern const char *fl_bg2;
+// end of extern additions workaround
+
+
+/**
+ Creates a driver that manages all screen and display related calls.
+
+ This function must be implemented once for every platform.
+ */
+Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver()
+{
+ return new Fl_Wayland_Screen_Driver();
+}
+
+FL_EXPORT struct wl_display *fl_display = NULL;
+
+static bool has_xrgb = false;
+
+
+static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ if (format == Fl_Wayland_Graphics_Driver::wld_format)
+ has_xrgb = true;
+}
+
+static struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void do_set_cursor(struct seat *seat, struct wl_cursor *wl_cursor = NULL)
+{
+ struct wl_cursor_image *image;
+ struct wl_buffer *buffer;
+ const int scale = seat->pointer_scale;
+
+ if (!seat->cursor_theme)
+ return;
+
+ if (!wl_cursor) wl_cursor = seat->default_cursor;
+ image = wl_cursor->images[0];
+ buffer = wl_cursor_image_get_buffer(image);
+ wl_pointer_set_cursor(seat->wl_pointer, seat->serial,
+ seat->cursor_surface,
+ image->hotspot_x / scale,
+ image->hotspot_y / scale);
+ wl_surface_attach(seat->cursor_surface, buffer, 0, 0);
+ wl_surface_set_buffer_scale(seat->cursor_surface, scale);
+ wl_surface_damage_buffer(seat->cursor_surface, 0, 0,
+ image->width, image->height);
+ wl_surface_commit(seat->cursor_surface);
+}
+
+static uint32_t ptime;
+FL_EXPORT uint32_t fl_event_time;
+static int px, py;
+
+
+static void set_event_xy(Fl_Window *win) {
+ // turn off is_click if enough time or mouse movement has passed:
+ if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 ||
+ fl_event_time >= ptime+1000) {
+ Fl::e_is_click = 0;
+//fprintf(stderr, "Fl::e_is_click = 0\n");
+ }
+}
+
+// if this is same event as last && is_click, increment click count:
+static inline void checkdouble() {
+ if (Fl::e_is_click == Fl::e_keysym) {
+ Fl::e_clicks++;
+//fprintf(stderr, "Fl::e_clicks = %d\n", Fl::e_clicks);
+ } else {
+ Fl::e_clicks = 0;
+ Fl::e_is_click = Fl::e_keysym;
+//fprintf(stderr, "Fl::e_is_click = %d\n", Fl::e_is_click);
+ }
+ px = Fl::e_x_root;
+ py = Fl::e_y_root;
+ ptime = fl_event_time;
+}
+
+
+Fl_Window *Fl_Wayland_Screen_Driver::surface_to_window(struct wl_surface *surface) {
+ Fl_X *xp = Fl_X::first;
+ while (xp) {
+ if (xp->xid->wl_surface == surface) return xp->w;
+ xp = xp->next;
+ }
+ return NULL;
+}
+
+
+static void pointer_enter(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t serial,
+ struct wl_surface *surface,
+ wl_fixed_t surface_x,
+ wl_fixed_t surface_y)
+{
+ struct seat *seat = (struct seat*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ struct wl_cursor *cursor = NULL;
+ if (win) { // use custom cursor if present
+ Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win);
+ cursor = driver->cursor();
+ if (win->parent() && !cursor) {
+ driver = Fl_Wayland_Window_Driver::driver(win->top_window());
+ cursor = driver->cursor();
+ }
+ }
+ do_set_cursor(seat, cursor);
+ seat->serial = serial;
+ if (win) {
+ float f = Fl::screen_scale(win->screen_num());
+ Fl::e_x = wl_fixed_to_int(surface_x) / f;
+ Fl::e_x_root = Fl::e_x + win->x();
+ Fl::e_y = wl_fixed_to_int(surface_y) / f;
+ Fl::e_y_root = Fl::e_y + win->y();
+ set_event_xy(win);
+ Fl::handle(FL_ENTER, win);
+//fprintf(stderr, "pointer_enter window=%p\n", win);
+ }
+ seat->pointer_focus = surface;
+}
+
+
+static void pointer_leave(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t serial,
+ struct wl_surface *surface)
+{
+ struct seat *seat = (struct seat*)data;
+ if (seat->pointer_focus == surface) seat->pointer_focus = NULL;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ if (win) {
+ Fl::belowmouse(0);
+ set_event_xy(win);
+ }
+//fprintf(stderr, "pointer_leave surface=%p window=%p\n", surface, win);
+}
+
+
+static void pointer_motion(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t time,
+ wl_fixed_t surface_x,
+ wl_fixed_t surface_y)
+{
+ struct seat *seat = (struct seat*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(seat->pointer_focus);
+ if (!win) return;
+ float f = Fl::screen_scale(win->screen_num());
+ Fl::e_x = wl_fixed_to_int(surface_x) / f;
+ Fl::e_x_root = Fl::e_x + win->x();
+ // If there's an active grab() and the pointer is in a window other than the grab(),
+ // make e_x_root too large to be in any window
+ if (Fl::grab() && !Fl::grab()->menu_window() && Fl::grab() != win) {
+ Fl::e_x_root = 1000000;
+ }
+ Fl::e_y = wl_fixed_to_int(surface_y) / f;
+ Fl::e_y_root = Fl::e_y + win->y();
+//fprintf(stderr, "FL_MOVE on win=%p to x:%dx%d root:%dx%d\n", win, Fl::e_x, Fl::e_y, Fl::e_x_root, Fl::e_y_root);
+ fl_event_time = time;
+ set_event_xy(win);
+ Fl::handle(FL_MOVE, win);
+}
+
+
+//#include <FL/names.h>
+static void pointer_button(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t button,
+ uint32_t state)
+{
+ struct seat *seat = (struct seat*)data;
+ seat->serial = serial;
+ int event = 0;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(seat->pointer_focus);
+ if (!win) return;
+ fl_event_time = time;
+ if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED && seat->pointer_focus == NULL &&
+ fl_xid(win)->kind == Fl_Wayland_Window_Driver::DECORATED) {
+ // click on titlebar
+ libdecor_frame_move(fl_xid(win)->frame, seat->wl_seat, serial);
+ return;
+ }
+ int b = 0;
+ Fl::e_state = 0;
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ if (button == BTN_LEFT) {Fl::e_state = FL_BUTTON1; b = 1;}
+ else if (button == BTN_RIGHT) {Fl::e_state = FL_BUTTON3; b = 3;}
+ else if (button == BTN_MIDDLE) {Fl::e_state = FL_BUTTON2; b = 2;}
+ Fl::e_keysym = FL_Button + b;
+ }
+ Fl::e_dx = Fl::e_dy = 0;
+
+ set_event_xy(win);
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ event = FL_PUSH;
+ checkdouble();
+ } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) event = FL_RELEASE;
+//fprintf(stderr, "%s %s\n", fl_eventnames[event], win->label() ? win->label():"[]");
+ Fl::handle(event, win);
+}
+
+static void pointer_axis(void *data,
+ struct wl_pointer *wl_pointer,
+ uint32_t time,
+ uint32_t axis,
+ wl_fixed_t value)
+{
+ struct seat *seat = (struct seat*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(seat->pointer_focus);
+ if (!win) return;
+ fl_event_time = time;
+ int delta = wl_fixed_to_int(value) / 10;
+//fprintf(stderr, "FL_MOUSEWHEEL: %c delta=%d\n", axis==WL_POINTER_AXIS_HORIZONTAL_SCROLL?'H':'V', delta);
+ // allow both horizontal and vertical movements to be processed by the widget
+ if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
+ Fl::e_dx = delta;
+ Fl::e_dy = 0;
+ Fl::handle(FL_MOUSEWHEEL, win->top_window());
+ }
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ Fl::e_dx = 0;
+ Fl::e_dy = delta;
+ Fl::handle(FL_MOUSEWHEEL, win->top_window());
+ }
+}
+
+static struct wl_pointer_listener pointer_listener = {
+ pointer_enter,
+ pointer_leave,
+ pointer_motion,
+ pointer_button,
+ pointer_axis
+};
+
+static const char *proxy_tag = "FLTK for Wayland";
+
+bool Fl_Wayland_Screen_Driver::own_output(struct wl_output *output)
+{
+ return wl_proxy_get_tag((struct wl_proxy *)output) == &proxy_tag;
+}
+
+static void init_cursors(struct seat *seat);
+
+static void try_update_cursor(struct seat *seat)
+{
+ struct pointer_output *pointer_output;
+ int scale = 1;
+
+ wl_list_for_each(pointer_output, &seat->pointer_outputs, link) {
+ scale = fl_max(scale, pointer_output->output->wld_scale);
+ }
+
+ if (scale != seat->pointer_scale) {
+ seat->pointer_scale = scale;
+ init_cursors(seat);
+ do_set_cursor(seat);
+ }
+}
+
+
+static void cursor_surface_enter(void *data,
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
+{
+ struct seat *seat = (struct seat*)data;
+ struct pointer_output *pointer_output;
+
+ if (!Fl_Wayland_Screen_Driver::own_output(wl_output))
+ return;
+
+ pointer_output = (struct pointer_output *)calloc(1, sizeof(struct pointer_output));
+ pointer_output->output = (Fl_Wayland_Screen_Driver::output *)wl_output_get_user_data(wl_output);
+//fprintf(stderr, "cursor_surface_enter: wl_output_get_user_data(%p)=%p\n", wl_output, pointer_output->output);
+ wl_list_insert(&seat->pointer_outputs, &pointer_output->link);
+ try_update_cursor(seat);
+ // maintain custom window cursor
+ Fl_Window *win = Fl::first_window();
+ if (win) {
+ Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win);
+ struct wl_cursor *cursor = driver->cursor();
+ if (cursor) do_set_cursor(seat, cursor);
+ }
+}
+
+static void cursor_surface_leave(void *data,
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
+{
+ struct seat *seat = (struct seat*)data;
+ struct pointer_output *pointer_output, *tmp;
+
+ wl_list_for_each_safe(pointer_output, tmp, &seat->pointer_outputs, link) {
+ if (pointer_output->output->wl_output == wl_output) {
+ wl_list_remove(&pointer_output->link);
+ free(pointer_output);
+ }
+ }
+}
+
+static struct wl_surface_listener cursor_surface_listener = {
+ cursor_surface_enter,
+ cursor_surface_leave,
+};
+
+
+static void init_cursors(struct seat *seat)
+{
+ char *name;
+ int size;
+ struct wl_cursor_theme *theme;
+
+ if (!libdecor_get_cursor_settings(&name, &size)) {
+ name = NULL;
+ size = 24;
+ }
+ size *= seat->pointer_scale;
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ theme = wl_cursor_theme_load(name, size, scr_driver->wl_shm);
+ free(name);
+ //struct wl_cursor_theme *old_theme = seat->cursor_theme;
+ if (theme != NULL) {
+ if (seat->cursor_theme) {
+ // caution to destroy theme because Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor) caches used cursors
+ scr_driver->reset_cursor();
+ wl_cursor_theme_destroy(seat->cursor_theme);
+ }
+ seat->cursor_theme = theme;
+ }
+ if (seat->cursor_theme)
+ seat->default_cursor = scr_driver->xc_arrow = wl_cursor_theme_get_cursor(seat->cursor_theme, "left_ptr");
+ if (!seat->cursor_surface) {
+ seat->cursor_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
+ wl_surface_add_listener(seat->cursor_surface, &cursor_surface_listener, seat);
+ }
+}
+
+
+static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t format, int32_t fd, uint32_t size)
+{
+ struct seat *seat = (struct seat*)data;
+ assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
+
+ char *map_shm = (char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ assert(map_shm != MAP_FAILED);
+
+ struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(seat->xkb_context, map_shm,
+ XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ munmap(map_shm, size);
+ close(fd);
+
+ struct xkb_state *xkb_state = xkb_state_new(xkb_keymap);
+ xkb_keymap_unref(seat->xkb_keymap);
+ xkb_state_unref(seat->xkb_state);
+ seat->xkb_keymap = xkb_keymap;
+ seat->xkb_state = xkb_state;
+}
+
+static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ struct seat *seat = (struct seat*)data;
+//fprintf(stderr, "keyboard enter fl_win=%p; keys pressed are:\n", Fl_Wayland_Screen_Driver::surface_to_window(surface));
+ seat->keyboard_surface = surface;
+ seat->keyboard_enter_serial = serial;
+}
+
+struct key_repeat_data_t {
+ uint32_t time;
+ Fl_Window *window;
+};
+
+#define KEY_REPEAT_DELAY 0.5 // sec
+#define KEY_REPEAT_INTERVAL 0.05 // sec
+
+static void key_repeat_timer_cb(key_repeat_data_t *key_repeat_data) {
+ if ((Fl::event() == FL_KEYDOWN || (Fl_Window_Driver::menu_parent() && Fl::event() == FL_ENTER)) && fl_event_time == key_repeat_data->time) {
+ Fl::handle(FL_KEYDOWN, key_repeat_data->window);
+ Fl::add_timeout(KEY_REPEAT_INTERVAL, (Fl_Timeout_Handler)key_repeat_timer_cb, key_repeat_data);
+ }
+ else delete key_repeat_data;
+}
+
+int Fl_Wayland_Screen_Driver::next_marked_length = 0;
+
+int Fl_Wayland_Screen_Driver::has_marked_text() const {
+ return 1;
+}
+
+int Fl_Wayland_Screen_Driver::insertion_point_x = 0;
+int Fl_Wayland_Screen_Driver::insertion_point_y = 0;
+int Fl_Wayland_Screen_Driver::insertion_point_width = 0;
+int Fl_Wayland_Screen_Driver::insertion_point_height = 0;
+bool Fl_Wayland_Screen_Driver::insertion_point_location_is_valid = false;
+
+
+// inform TIM about location of the insertion point, and memorize this info.
+void Fl_Wayland_Screen_Driver::insertion_point_location(int x, int y, int height) {
+//printf("insertion_point_location %dx%d\n",x,y);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (scr_driver->seat->text_input) {
+ if (Fl::focus()) {
+ Fl_Widget *focuswin = Fl::focus()->window();
+ while (focuswin && focuswin->parent()) {
+ x += focuswin->x(); y += focuswin->y();
+ focuswin = focuswin->window();
+ }
+ }
+ float s = fl_graphics_driver->scale();
+ insertion_point_location_is_valid = true;
+ insertion_point_x = s*x;
+ insertion_point_y = s*(y-height);
+ insertion_point_width = s*5;
+ insertion_point_height = s*height;
+ if (zwp_text_input_v3_get_user_data(scr_driver->seat->text_input) ) {
+ zwp_text_input_v3_set_cursor_rectangle(scr_driver->seat->text_input, insertion_point_x,
+ insertion_point_y, insertion_point_width, insertion_point_height);
+ zwp_text_input_v3_commit(scr_driver->seat->text_input);
+ }
+ }
+}
+
+
+// computes window coordinates & size of insertion point
+bool Fl_Wayland_Screen_Driver::insertion_point_location(int *px, int *py, int *pwidth, int *pheight)
+// return true if the current coordinates and size of the insertion point are available
+{
+ if ( ! insertion_point_location_is_valid ) return false;
+ *px = insertion_point_x;
+ *py = insertion_point_y;
+ *pwidth = insertion_point_width;
+ *pheight = insertion_point_height;
+ return true;
+}
+
+int Fl_Wayland_Screen_Driver::compose(int& del) {
+ unsigned char ascii = (unsigned char)Fl::e_text[0];
+ int condition = (Fl::e_state & (FL_ALT | FL_META | FL_CTRL)) && ascii < 128 ; // letter+modifier key
+ condition |= (Fl::e_keysym >= FL_Shift_L && Fl::e_keysym <= FL_Alt_R); // pressing modifier key
+ condition |= (Fl::e_keysym >= FL_Home && Fl::e_keysym <= FL_Help);
+//fprintf(stderr, "compose: condition=%d e_state=%x ascii=%d\n", condition, Fl::e_state, ascii);
+ if (condition) { del = 0; return 0;}
+//fprintf(stderr, "compose: del=%d compose_state=%d next_marked_length=%d \n", del, Fl::compose_state, next_marked_length);
+ del = Fl::compose_state;
+ Fl::compose_state = next_marked_length;
+ // no-underlined-text && (ascii non-printable || ascii == delete)
+ if (ascii && (!Fl::compose_state) && (ascii <= 31 || ascii == 127)) { del = 0; return 0; }
+ return 1;
+}
+
+void Fl_Wayland_Screen_Driver::compose_reset()
+{
+ Fl::compose_state = 0;
+ next_marked_length = 0;
+ xkb_compose_state_reset(seat->xkb_compose_state);
+}
+
+struct dead_key_struct {
+ xkb_keysym_t keysym; // the keysym obtained when hitting a dead key
+ const char *marked_text; // the temporary text to display for that dead key
+};
+
+static dead_key_struct dead_keys[] = {
+ {XKB_KEY_dead_grave, "`"},
+ {XKB_KEY_dead_acute, "´"},
+ {XKB_KEY_dead_circumflex, "^"},
+ {XKB_KEY_dead_tilde, "~"},
+ {XKB_KEY_dead_perispomeni, "~"}, // alias for dead_tilde
+ {XKB_KEY_dead_macron, "¯"},
+ {XKB_KEY_dead_breve, "˘"},
+ {XKB_KEY_dead_abovedot, "˙"},
+ {XKB_KEY_dead_diaeresis, "¨"},
+ {XKB_KEY_dead_abovering, "˚"},
+ {XKB_KEY_dead_doubleacute, "˝"},
+ {XKB_KEY_dead_caron, "ˇ"},
+ {XKB_KEY_dead_cedilla, "¸"},
+ {XKB_KEY_dead_ogonek, "˛"},
+ {XKB_KEY_dead_iota, "ι"},
+ {XKB_KEY_dead_doublegrave, " ̏"},
+};
+
+const int dead_key_count = sizeof(dead_keys)/sizeof(struct dead_key_struct);
+
+static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
+{
+ struct seat *seat = (struct seat*)data;
+ seat->serial = serial;
+ static char buf[128];
+ uint32_t keycode = key + 8;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->xkb_state, keycode);
+/*xkb_keysym_get_name(sym, buf, sizeof(buf));
+const char *action = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release");
+fprintf(stderr, "key %s: sym: %-12s(%d) code:%u fl_win=%p, ", action, buf, sym, keycode, Fl_Wayland_Screen_Driver::surface_to_window(seat->keyboard_surface));*/
+ xkb_state_key_get_utf8(seat->xkb_state, keycode, buf, sizeof(buf));
+//fprintf(stderr, "utf8: '%s' e_length=%d [%d]\n", buf, (int)strlen(buf), *buf);
+ Fl::e_keysym = sym;
+ // special processing for number keys == keycodes 10-19 :
+ if (keycode >= 10 && keycode <= 18) Fl::e_keysym = keycode + 39;
+ else if (keycode == 19) Fl::e_keysym = 48;
+ Fl::e_text = buf;
+ Fl::e_length = strlen(buf);
+ // Process dead keys and compose sequences :
+ enum xkb_compose_status status = XKB_COMPOSE_NOTHING;
+ // This part is useful only if the compositor doesn't support protocol text-input-unstable-v3
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED && !(sym >= FL_Shift_L && sym <= FL_Alt_R) &&
+ sym != XKB_KEY_ISO_Level3_Shift) {
+ xkb_compose_state_feed(seat->xkb_compose_state, sym);
+ status = xkb_compose_state_get_status(seat->xkb_compose_state);
+ if (status == XKB_COMPOSE_COMPOSING) {
+ if (Fl::e_length == 0) { // dead keys produce e_length = 0
+ int i;
+ for (i = 0; i < dead_key_count; i++) {
+ if (dead_keys[i].keysym == sym) break;
+ }
+ if (i < dead_key_count) strcpy(buf, dead_keys[i].marked_text);
+ else buf[0] = 0;
+ Fl::e_length = strlen(buf);
+ Fl::compose_state = 0;
+ }
+ Fl_Wayland_Screen_Driver::next_marked_length = Fl::e_length;
+ } else if (status == XKB_COMPOSE_COMPOSED) {
+ Fl::e_length = xkb_compose_state_get_utf8(seat->xkb_compose_state, buf, sizeof(buf));
+ Fl::compose_state = Fl_Wayland_Screen_Driver::next_marked_length;
+ Fl_Wayland_Screen_Driver::next_marked_length = 0;
+ } else if (status == XKB_COMPOSE_CANCELLED) {
+ Fl::e_length = 0;
+ Fl::compose_state = Fl_Wayland_Screen_Driver::next_marked_length;
+ Fl_Wayland_Screen_Driver::next_marked_length = 0;
+ }
+//fprintf(stderr, "xkb_compose_status=%d ctxt=%p state=%p l=%d[%s]\n", status, seat->xkb_context, seat->xkb_compose_state, Fl::e_length, buf);
+ }
+ // end of part used only without text-input-unstable-v3
+
+ fl_event_time = time;
+ int event = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? FL_KEYDOWN : FL_KEYUP);
+ // Send event to focus-containing top window as defined by FLTK,
+ // otherwise send it to Wayland-defined focus window
+ Fl_Window *win = ( Fl::focus() ? Fl::focus()->top_window() : Fl_Wayland_Screen_Driver::surface_to_window(seat->keyboard_surface) );
+ if (win) {
+ set_event_xy(win);
+ Fl::e_is_click = 0;
+ Fl::handle(event, win);
+ }
+ key_repeat_data_t *key_repeat_data = new key_repeat_data_t;
+ key_repeat_data->time = time;
+ key_repeat_data->window = win;
+ if (event == FL_KEYDOWN && status == XKB_COMPOSE_NOTHING && !(sym >= FL_Shift_L && sym <= FL_Alt_R))
+ Fl::add_timeout(KEY_REPEAT_DELAY, (Fl_Timeout_Handler)key_repeat_timer_cb, key_repeat_data);
+}
+
+static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct seat *seat = (struct seat*)data;
+//fprintf(stderr, "keyboard leave fl_win=%p\n", Fl_Wayland_Screen_Driver::surface_to_window(surface));
+ seat->keyboard_surface = NULL;
+}
+
+static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+ struct seat *seat = (struct seat*)data;
+ xkb_state_update_mask(seat->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+ Fl::e_state = 0;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_DEPRESSED))
+ Fl::e_state |= FL_SHIFT;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_DEPRESSED))
+ Fl::e_state |= FL_CTRL;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_DEPRESSED))
+ Fl::e_state |= FL_ALT;
+ if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED))
+ Fl::e_state |= FL_CAPS_LOCK;
+//fprintf(stderr, "mods_depressed=%u Fl::e_state=%X\n", mods_depressed, Fl::e_state);
+}
+
+static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay)
+{
+ // wl_keyboard is version 3 under Debian, but that event isn't sent until version 4
+}
+
+static const struct wl_keyboard_listener wl_keyboard_listener = {
+ .keymap = wl_keyboard_keymap,
+ .enter = wl_keyboard_enter,
+ .leave = wl_keyboard_leave,
+ .key = wl_keyboard_key,
+ .modifiers = wl_keyboard_modifiers,
+ .repeat_info = wl_keyboard_repeat_info,
+};
+
+
+void text_input_enter(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ struct wl_surface *surface) {
+//puts("text_input_enter");
+ zwp_text_input_v3_set_user_data(zwp_text_input_v3, surface);
+ zwp_text_input_v3_enable(zwp_text_input_v3);
+ int x, y, width, height;
+ if (Fl_Wayland_Screen_Driver::insertion_point_location(&x, &y, &width, &height)) {
+ zwp_text_input_v3_set_cursor_rectangle(zwp_text_input_v3, x, y, width, height);
+ }
+ zwp_text_input_v3_commit(zwp_text_input_v3);
+ wl_display_roundtrip(fl_display);
+}
+
+void text_input_leave(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ struct wl_surface *surface) {
+//puts("text_input_leave");
+ zwp_text_input_v3_disable(zwp_text_input_v3);
+ zwp_text_input_v3_set_user_data(zwp_text_input_v3, NULL);
+ zwp_text_input_v3_commit(zwp_text_input_v3);
+}
+
+void text_input_preedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ const char *text, int32_t cursor_begin, int32_t cursor_end) {
+//printf("text_input_preedit_string %s cursor_begin=%d cursor_end=%d\n",text, cursor_begin, cursor_end);
+ // goes to widget as marked text
+ Fl_Wayland_Screen_Driver::next_marked_length = text ? strlen(text) : 0;
+ Fl::e_text = text ? (char*)text : (char*)"";
+ Fl::e_length = text ? strlen(text) : 0;
+ Fl::e_keysym = 'a'; // fake a simple key
+ struct wl_surface *surface = (struct wl_surface*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ set_event_xy(win);
+ Fl::e_is_click = 0;
+ Fl::handle(FL_KEYDOWN, win);
+}
+
+void text_input_commit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ const char *text) {
+//printf("text_input_commit_string %s\n",text);
+ Fl::e_text = (char*)text;
+ Fl::e_length = strlen(text);
+ struct wl_surface *surface = (struct wl_surface*)data;
+ Fl_Window *win = Fl_Wayland_Screen_Driver::surface_to_window(surface);
+ set_event_xy(win);
+ Fl::e_is_click = 0;
+ Fl::handle(FL_KEYDOWN, win);
+ zwp_text_input_v3_commit(zwp_text_input_v3);
+ Fl_Wayland_Screen_Driver::next_marked_length = 0;
+ Fl::compose_state = 0;
+}
+
+void text_input_delete_surrounding_text(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ uint32_t before_length, uint32_t after_length) {
+ fprintf(stderr, "delete_surrounding_text before=%d adfter=%d\n",before_length,after_length);
+}
+
+void text_input_done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
+ uint32_t serial) {
+//puts("text_input_done");
+}
+
+static const struct zwp_text_input_v3_listener text_input_listener = {
+ .enter = text_input_enter,
+ .leave = text_input_leave,
+ .preedit_string = text_input_preedit_string,
+ .commit_string = text_input_commit_string,
+ .delete_surrounding_text = text_input_delete_surrounding_text,
+ .done = text_input_done,
+};
+
+
+static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
+{
+ struct seat *seat = (struct seat*)data;
+ if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) {
+ seat->wl_pointer = wl_seat_get_pointer(wl_seat);
+ wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
+ seat->pointer_scale = 1;
+ init_cursors(seat);
+ } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) {
+ wl_pointer_release(seat->wl_pointer);
+ seat->wl_pointer = NULL;
+ }
+
+ bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;
+ if (have_keyboard && seat->wl_keyboard == NULL) {
+ seat->wl_keyboard = wl_seat_get_keyboard(wl_seat);
+ wl_keyboard_add_listener(seat->wl_keyboard,
+ &wl_keyboard_listener, seat);
+//fprintf(stderr, "wl_keyboard version=%d\n", wl_keyboard_get_version(seat->wl_keyboard));
+
+ } else if (!have_keyboard && seat->wl_keyboard != NULL) {
+ wl_keyboard_release(seat->wl_keyboard);
+ seat->wl_keyboard = NULL;
+ }
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (scr_driver->text_input_base) {
+ seat->text_input = zwp_text_input_manager_v3_get_text_input(scr_driver->text_input_base, seat->wl_seat);
+//printf("seat->text_input=%p\n",seat->text_input);
+ zwp_text_input_v3_add_listener(seat->text_input, &text_input_listener, NULL);
+ }
+}
+
+static void seat_name(void *data, struct wl_seat *wl_seat, const char *name) {
+ struct seat *seat = (struct seat*)data;
+ seat->name = strdup(name);
+}
+
+static struct wl_seat_listener seat_listener = {
+ seat_capabilities,
+ seat_name
+};
+
+static void output_geometry(void *data,
+ struct wl_output *wl_output,
+ int32_t x,
+ int32_t y,
+ int32_t physical_width,
+ int32_t physical_height,
+ int32_t subpixel,
+ const char *make,
+ const char *model,
+ int32_t transform)
+{
+ //fprintf(stderr, "output_geometry: x=%d y=%d physical=%dx%d\n",x,y,physical_width,physical_height);
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ output->dpi = 96; // to elaborate
+}
+
+static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
+ int32_t width, int32_t height, int32_t refresh)
+{
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ output->x_org = 0;
+ output->y_org = 0;
+ output->width = width;
+ output->height = height;
+//fprintf(stderr, "output_mode: [%p]=%dx%d\n",output->wl_output,width,height);
+}
+
+static void output_done(void *data, struct wl_output *wl_output)
+{
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ Fl_Wayland_Window_Driver::window_output *window_output;
+ struct seat *seat;
+//fprintf(stderr, "output_done output=%p\n",output);
+ Fl_X *xp = Fl_X::first;
+ while (xp) { // all mapped windows
+ struct wld_window *win = xp->xid;
+ wl_list_for_each(window_output, &(win->outputs), link) { // all Fl_Wayland_Window_Driver::window_output for this window
+ if (window_output->output == output) {
+ Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(win->fl_win);
+ if (output->wld_scale != win->scale) win_driver->update_scale();
+ }
+ }
+ xp = xp->next;
+ }
+
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ wl_list_for_each(seat, &(scr_driver->seats), link) {
+ try_update_cursor(seat);
+ }
+ scr_driver->init_workarea();
+ Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL);
+}
+
+
+static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) {
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
+ output->wld_scale = factor;
+//fprintf(stderr,"output_scale: wl_output=%p factor=%d\n",wl_output, factor);
+}
+
+
+static struct wl_output_listener output_listener = {
+ output_geometry,
+ output_mode,
+ output_done,
+ output_scale
+};
+
+
+static void registry_handle_global(void *user_data, struct wl_registry *wl_registry,
+ uint32_t id, const char *interface, uint32_t version) {
+//fprintf(stderr, "interface=%s\n", interface);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (strcmp(interface, "wl_compositor") == 0) {
+ if (version < 4) {
+ Fl::fatal("wl_compositor version >= 4 required");
+ }
+ scr_driver->wl_compositor = (struct wl_compositor*)wl_registry_bind(wl_registry,
+ id, &wl_compositor_interface, 4);
+
+ } else if (strcmp(interface, "wl_subcompositor") == 0) {
+ scr_driver->wl_subcompositor = (struct wl_subcompositor*)wl_registry_bind(wl_registry,
+ id, &wl_subcompositor_interface, 1);
+
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ scr_driver->wl_shm = (struct wl_shm*)wl_registry_bind(wl_registry,
+ id, &wl_shm_interface, 1);
+ wl_shm_add_listener(scr_driver->wl_shm, &shm_listener, NULL);
+
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ if (version < 3) {
+ Fl::fatal("%s version 3 required but only version %i is available\n", interface, version);
+ }
+ if (!scr_driver->seat) scr_driver->seat = (struct seat*)calloc(1, sizeof(struct seat));
+//fprintf(stderr, "registry_handle_global: seat=%p\n", scr_driver->seat);
+ wl_list_init(&scr_driver->seat->pointer_outputs);
+ scr_driver->seat->wl_seat = (wl_seat*)wl_registry_bind(wl_registry, id, &wl_seat_interface, 3);
+ scr_driver->seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ const char *locale = getenv("LC_ALL");
+ if (!locale || !*locale)
+ locale = getenv("LC_CTYPE");
+ if (!locale || !*locale)
+ locale = getenv("LANG");
+ if (!locale || !*locale)
+ locale = "C";
+ struct xkb_compose_table *table = xkb_compose_table_new_from_locale(scr_driver->seat->xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
+ scr_driver->seat->xkb_compose_state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
+ wl_seat_add_listener(scr_driver->seat->wl_seat, &seat_listener, scr_driver->seat);
+ if (scr_driver->seat->data_device_manager) {
+ scr_driver->seat->data_device = wl_data_device_manager_get_data_device(scr_driver->seat->data_device_manager, scr_driver->seat->wl_seat);
+ wl_data_device_add_listener(scr_driver->seat->data_device, Fl_Wayland_Screen_Driver::p_data_device_listener, NULL);
+ }
+
+ } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
+ if (!scr_driver->seat) scr_driver->seat = (struct seat*)calloc(1, sizeof(struct seat));
+ scr_driver->seat->data_device_manager = (struct wl_data_device_manager*)wl_registry_bind(wl_registry, id, &wl_data_device_manager_interface, 3);
+ if (scr_driver->seat->wl_seat) {
+ scr_driver->seat->data_device = wl_data_device_manager_get_data_device(scr_driver->seat->data_device_manager, scr_driver->seat->wl_seat);
+ wl_data_device_add_listener(scr_driver->seat->data_device, Fl_Wayland_Screen_Driver::p_data_device_listener, NULL);
+ }
+//fprintf(stderr, "registry_handle_global: %s\n", interface);
+
+ } else if (strcmp(interface, "wl_output") == 0) {
+ if (version < 2) {
+ Fl::fatal("%s version 3 required but only version %i is available\n", interface, version);
+ }
+ Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)calloc(1, sizeof *output);
+ output->id = id;
+ output->wld_scale = 1;
+ output->wl_output = (struct wl_output*)wl_registry_bind(wl_registry,
+ id, &wl_output_interface, 2);
+ output->gui_scale = 1.f;
+ wl_proxy_set_tag((struct wl_proxy *) output->wl_output, &proxy_tag);
+ wl_output_add_listener(output->wl_output, &output_listener, output);
+ wl_list_insert(&(scr_driver->outputs), &output->link);
+ scr_driver->screen_count( wl_list_length(&(scr_driver->outputs)) );
+//fprintf(stderr, "wl_output: id=%d wl_output=%p screen_count()=%d\n", id, output->wl_output, Fl::screen_count());
+
+ } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
+//fprintf(stderr, "registry_handle_global interface=%s\n", interface);
+ scr_driver->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, id, &xdg_wm_base_interface, 1);
+ xdg_wm_base_add_listener(scr_driver->xdg_wm_base, &xdg_wm_base_listener, NULL);
+ } else if (strcmp(interface, "gtk_shell1") == 0) {
+ Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::MUTTER;
+ //fprintf(stderr, "Running the Mutter compositor\n");
+ } else if (strcmp(interface, "weston_desktop_shell") == 0) {
+ Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::WESTON;
+ //fprintf(stderr, "Running the Weston compositor\n");
+ } else if (strcmp(interface, "org_kde_plasma_shell") == 0) {
+ Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::KDE;
+ //fprintf(stderr, "Running the KDE compositor\n");
+ }
+ else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
+ scr_driver->text_input_base = (struct zwp_text_input_manager_v3 *) wl_registry_bind(wl_registry, id, &zwp_text_input_manager_v3_interface, 1);
+ //printf("scr_driver->text_input_base=%p version=%d\n",scr_driver->text_input_base,version);
+ }
+}
+
+
+static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
+{//TODO to be tested
+ Fl_Wayland_Screen_Driver::output *output;
+ Fl_Wayland_Window_Driver::window_output *window_output, *tmp;
+//fprintf(stderr, "registry_handle_global_remove data=%p id=%u\n", data, name);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ wl_list_for_each(output, &(scr_driver->outputs), link) { // all screens of the system
+ if (output->id == name) { // the screen being removed
+ Fl_X *xp = Fl_X::first;
+ while (xp) { // all mapped windows
+ struct wld_window *win = xp->xid;
+ wl_list_for_each_safe(window_output, tmp, &(win->outputs), link) { // all Fl_Wayland_Window_Driver::window_output for this window
+ if (window_output->output == output) {
+ wl_list_remove(&window_output->link);
+ free(window_output);
+ }
+ }
+ xp = xp->next;
+ }
+ wl_list_remove(&output->link);
+ scr_driver->screen_count( wl_list_length(&(scr_driver->outputs)) );
+ wl_output_destroy(output->wl_output);
+ free(output);
+ break;
+ }
+ }
+}
+
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+
+static void fd_callback(int unused, struct wl_display *display) {
+ wl_display_dispatch(display);
+}
+
+
+Fl_Wayland_Screen_Driver::Fl_Wayland_Screen_Driver() : Fl_Screen_Driver() {
+ libdecor_context = NULL;
+ seat = NULL;
+ text_input_base = NULL;
+ reset_cursor();
+}
+
+void Fl_Wayland_Screen_Driver::open_display_platform() {
+ struct wl_display *wl_display;
+ struct wl_registry *wl_registry;
+
+ static bool beenHereDoneThat = false;
+ if (beenHereDoneThat)
+ return;
+
+ beenHereDoneThat = true;
+ wl_display = wl_display_connect(NULL);
+ if (!wl_display) {
+ Fl::fatal("No Wayland connection\n");
+ }
+ fl_display = wl_display;
+ wl_list_init(&seats);
+ wl_list_init(&outputs);
+
+ wl_registry = wl_display_get_registry(wl_display);
+ wl_registry_add_listener(wl_registry, &registry_listener, NULL);
+ wl_display_dispatch(wl_display);
+ wl_display_roundtrip(wl_display);
+ if (!has_xrgb) {
+ Fl::fatal("Error: no WL_SHM_FORMAT_ARGB8888 shm format\n");
+ }
+ if (compositor == Fl_Wayland_Screen_Driver::unspecified) {
+ Fl::warning("FLTK could not identify the type of the running Wayland compositor");
+ }
+ Fl::add_fd(wl_display_get_fd(wl_display), FL_READ, (Fl_FD_Handler)fd_callback, wl_display);
+ fl_create_print_window();
+}
+
+void Fl_Wayland_Screen_Driver::close_display() {
+ Fl::remove_fd(wl_display_get_fd(fl_display));
+ wl_display_disconnect(fl_display);
+}
+
+
+static int workarea_xywh[4] = { -1, -1, -1, -1 };
+
+
+void Fl_Wayland_Screen_Driver::init_workarea()
+{
+ workarea_xywh[0] = 0;
+ workarea_xywh[1] = 0;
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ workarea_xywh[2] = output->width; // pixels
+ workarea_xywh[3] = output->height; // pixels
+ break;
+ }
+}
+
+
+int Fl_Wayland_Screen_Driver::x() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[0] / (output->gui_scale * output->wld_scale);
+}
+
+int Fl_Wayland_Screen_Driver::y() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[1] / (output->gui_scale * output->wld_scale);
+}
+
+int Fl_Wayland_Screen_Driver::w() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[2] / (output->gui_scale * output->wld_scale);
+}
+
+int Fl_Wayland_Screen_Driver::h() {
+ if (!fl_display) open_display();
+ Fl_Wayland_Screen_Driver::output *output;
+ wl_list_for_each(output, &outputs, link) {
+ break;
+ }
+ return workarea_xywh[3] / (output->gui_scale * output->wld_scale);
+}
+
+
+void Fl_Wayland_Screen_Driver::init() {
+ if (!fl_display) open_display();
+}
+
+
+void Fl_Wayland_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n)
+{
+ if (num_screens < 0) init();
+ if (n < 0 || n >= num_screens) n = 0;
+ if (n == 0) { // for the main screen, these return the work area
+ X = Fl::x();
+ Y = Fl::y();
+ W = Fl::w();
+ H = Fl::h();
+ } else { // for other screens, work area is full screen,
+ screen_xywh(X, Y, W, H, n);
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n)
+{
+ if (num_screens < 0) init();
+
+ if ((n < 0) || (n >= num_screens))
+ n = 0;
+
+ if (num_screens > 0) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) { // n'th screen of the system
+ float s = output->gui_scale * output->wld_scale;
+ X = output->x_org / s;
+ Y = output->y_org / s;
+ W = output->width / s;
+ H = output->height / s;
+ break;
+ }
+ }
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::screen_dpi(float &h, float &v, int n)
+{
+ if (num_screens < 0) init();
+ h = v = 0.0f;
+
+ if (n >= 0 && n < num_screens) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) { // n'th screen of the system
+ h = output->dpi;
+ v = output->dpi;
+ break;
+ }
+ }
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::beep(int type)
+{
+ fprintf(stderr, "\007");
+}
+
+
+void Fl_Wayland_Screen_Driver::flush()
+{
+ if (fl_display) {
+ wl_display_flush(fl_display);
+ }
+}
+
+
+extern void fl_fix_focus(); // in Fl.cxx
+
+
+void Fl_Wayland_Screen_Driver::grab(Fl_Window* win)
+{
+ Fl_Window *fullscreen_win = NULL;
+ for (Fl_Window *W = Fl::first_window(); W; W = Fl::next_window(W)) {
+ if (W->fullscreen_active()) {
+ fullscreen_win = W;
+ break;
+ }
+ }
+ if (win) {
+ if (!Fl::grab()) {
+ }
+ Fl::grab_ = win; // FIXME: Fl::grab_ "should be private", but we need
+ // a way to *set* the variable from the driver!
+ } else {
+ if (Fl::grab()) {
+ // We must keep the grab in the non-EWMH fullscreen case
+ if (!fullscreen_win ) {
+ //XUngrabKeyboard(fl_display, fl_event_time);
+ }
+ //XUngrabPointer(fl_display, fl_event_time);
+ // this flush is done in case the picked menu item goes into
+ // an infinite loop, so we don't leave the X server locked up:
+ //XFlush(fl_display);
+ Fl::grab_ = 0; // FIXME: Fl::grab_ "should be private", but we need
+ // a way to *set* the variable from the driver!
+ fl_fix_focus();
+ }
+ }
+}
+
+
+static void set_selection_color(uchar r, uchar g, uchar b)
+{
+ Fl::set_color(FL_SELECTION_COLOR,r,g,b);
+}
+
+static void getsyscolor(const char *key1, const char* key2, const char *arg, const char *defarg, void (*func)(uchar,uchar,uchar))
+{
+ uchar r, g, b;
+ if (!arg) arg = defarg;
+ if (!Fl::screen_driver()->parse_color(arg, r, g, b))
+ Fl::error("Unknown color: %s", arg);
+ else
+ func(r, g, b);
+}
+
+
+void Fl_Wayland_Screen_Driver::get_system_colors()
+{
+ open_display();
+ const char* key1 = 0;
+ if (Fl::first_window()) key1 = Fl::first_window()->xclass();
+ if (!key1) key1 = "fltk";
+ if (!bg2_set)
+ getsyscolor("Text","background", fl_bg2, "#ffffff", Fl::background2);
+ if (!fg_set)
+ getsyscolor(key1, "foreground", fl_fg, "#000000", Fl::foreground);
+ if (!bg_set)
+ getsyscolor(key1, "background", fl_bg, "#c0c0c0", Fl::background);
+ getsyscolor("Text", "selectBackground", 0, "#000080", set_selection_color);
+}
+
+
+const char *Fl_Wayland_Screen_Driver::get_system_scheme()
+{
+ return getenv("FLTK_SCHEME");
+}
+
+
+Fl_RGB_Image *Fl_Wayland_Screen_Driver::read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win,
+ bool ignore, bool *p_ignore) {
+ Window xid = win ? fl_xid(win) : NULL;
+ struct fl_wld_buffer *buffer = win ? xid->buffer : (Fl_Offscreen)Fl_Surface_Device::surface()->driver()->gc();
+ float s = win ? xid->scale * scale(win->screen_num()) :
+ Fl_Surface_Device::surface()->driver()->scale();
+ int Xs, Ys, ws, hs;
+ if (s == 1) {
+ Xs = X; Ys = Y; ws = w; hs = h;
+ } else {
+ Xs = Fl_Scalable_Graphics_Driver::floor(X, s);
+ Ys = Fl_Scalable_Graphics_Driver::floor(Y, s);
+ ws = Fl_Scalable_Graphics_Driver::floor(X+w, s) - Xs;
+ hs = Fl_Scalable_Graphics_Driver::floor(Y+h, s) - Ys;
+ }
+ if (ws == 0 || hs == 0) return NULL;
+ uchar *data = new uchar[ws * hs * 3];
+ uchar *p = data, *q;
+ for (int j = 0; j < hs; j++) {
+ q = buffer->draw_buffer + (j+Ys) * buffer->stride + 4 * Xs;
+ for (int i = 0; i < ws; i++) {
+ *p++ = *(q+2); // R
+ *p++ = *(q+1); // G
+ *p++ = *q; // B
+ q += 4;
+ }
+ }
+ Fl_RGB_Image *rgb = new Fl_RGB_Image(data, ws, hs, 3);
+ rgb->alloc_array = 1;
+ return rgb;
+}
+
+
+void Fl_Wayland_Screen_Driver::offscreen_size(Fl_Offscreen off, int &width, int &height)
+{
+ width = off->width;
+ height = off->data_size / off->stride;
+}
+
+//NOTICE: returns -1 if x,y is not in any screen
+int Fl_Wayland_Screen_Driver::screen_num_unscaled(int x, int y)
+{
+ if (num_screens < 0) init();
+
+ Fl_Wayland_Screen_Driver::output *output;
+ int screen = 0;
+ wl_list_for_each(output, &outputs, link) {
+ int s = output->wld_scale;
+ int sx = output->x_org/s, sy = output->y_org/s, sw = output->width/s, sh = output->height/s;
+ if ((x >= sx) && (x < (sx+sw)) && (y >= sy) && (y < (sy+sh))) {
+ return screen;
+ }
+ screen++;
+ }
+ return -1;
+}
+
+float Fl_Wayland_Screen_Driver::scale(int n) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) break;
+ }
+ return output->gui_scale;
+}
+
+
+void Fl_Wayland_Screen_Driver::scale(int n, float f) {
+ Fl_Wayland_Screen_Driver::output *output;
+ int i = 0;
+ wl_list_for_each(output, &outputs, link) {
+ if (i++ == n) {
+ output->gui_scale = f;
+ return;
+ }
+ }
+}
+
+
+void Fl_Wayland_Screen_Driver::set_cursor() {
+ do_set_cursor(seat);
+}
+
+struct wl_cursor *Fl_Wayland_Screen_Driver::default_cursor() {
+ return seat->default_cursor;
+}
+
+void Fl_Wayland_Screen_Driver::default_cursor(struct wl_cursor *cursor) {
+ seat->default_cursor = cursor;
+}
+
+struct wl_cursor *Fl_Wayland_Screen_Driver::cache_cursor(const char *cursor_name) {
+ return wl_cursor_theme_get_cursor(seat->cursor_theme, cursor_name);
+}
+
+void Fl_Wayland_Screen_Driver::reset_cursor() {
+ xc_arrow = xc_ns = xc_wait = xc_insert = xc_hand = xc_help = xc_cross = xc_move = xc_north = xc_south = xc_west = xc_east = xc_we = xc_nesw = xc_nwse = xc_sw = xc_se = xc_ne = xc_nw = NULL;
+}
+
+uint32_t Fl_Wayland_Screen_Driver::get_serial() {
+ return seat->serial;
+}
+
+struct wl_seat*Fl_Wayland_Screen_Driver::get_wl_seat() {
+ return seat->wl_seat;
+}
+
+char *Fl_Wayland_Screen_Driver::get_seat_name() {
+ return seat->name;
+}
+
+struct xkb_keymap *Fl_Wayland_Screen_Driver::get_xkb_keymap() {
+ return seat->xkb_keymap;
+}
diff --git a/src/drivers/Wayland/Fl_Wayland_System_Driver.H b/src/drivers/Wayland/Fl_Wayland_System_Driver.H
new file mode 100644
index 000000000..d117f3823
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_System_Driver.H
@@ -0,0 +1,34 @@
+//
+// Definition of Wayland system driver
+// for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2010-2021 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_SYSTEM_DRIVER_H
+#define FL_WAYLAND_SYSTEM_DRIVER_H
+
+#include "../Unix/Fl_Unix_System_Driver.H"
+
+class FL_EXPORT Fl_Wayland_System_Driver : public Fl_Unix_System_Driver {
+public:
+ Fl_Wayland_System_Driver() : Fl_Unix_System_Driver() {
+ // Wayland system driver uses the default key table
+ }
+ virtual int need_menu_handle_part2() {return 0;}
+ int event_key(int k);
+ int get_key(int k);
+ virtual void *control_maximize_button(void *data);
+};
+
+#endif /* FL_WAYLAND_SYSTEM_DRIVER_H */
diff --git a/src/drivers/Wayland/Fl_Wayland_System_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_System_Driver.cxx
new file mode 100644
index 000000000..96a4c16cd
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_System_Driver.cxx
@@ -0,0 +1,98 @@
+//
+// Definition of Wayland system driver
+// for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2010-2021 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_System_Driver.H"
+#include <FL/Fl.H>
+#include "Fl_Wayland_Window_Driver.H"
+#include <FL/platform.H>
+#include "../../../libdecor/src/libdecor.h"
+
+/**
+ Creates a driver that manages all system related calls.
+
+ This function must be implemented once for every platform.
+ */
+Fl_System_Driver *Fl_System_Driver::newSystemDriver()
+{
+ return new Fl_Wayland_System_Driver();
+}
+
+
+int Fl_Wayland_System_Driver::event_key(int k) {
+ if (k > FL_Button && k <= FL_Button+8)
+ return Fl::event_state(8<<(k-FL_Button));
+ int sym = Fl::event_key();
+ if (sym >= 'a' && sym <= 'z' ) sym -= 32;
+ return (Fl::event() == FL_KEYDOWN || Fl::event() == FL_SHORTCUT) && sym == k;
+}
+
+
+int Fl_Wayland_System_Driver::get_key(int k) {
+ return event_key(k);
+}
+
+
+void *Fl_Wayland_System_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() &&
+ !(Fl_X::i(win)->xid->state & LIBDECOR_WINDOW_STATE_MAXIMIZED) ) {
+ win_dims *dim = new win_dims;
+ dim->tracker = new Fl_Widget_Tracker(win);
+ Fl_Window_Driver *dr = Fl_Window_Driver::driver(win);
+ dim->minw = dr->minw();
+ dim->minh = dr->minh();
+ dim->maxw = dr->maxw();
+ dim->maxh = dr->maxh();
+ //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;
+ }
+}
diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H
new file mode 100644
index 000000000..99a67dbba
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H
@@ -0,0 +1,172 @@
+//
+// Definition of Wayland window driver for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2010-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
+//
+
+/**
+ \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 "../../Fl_Window_Driver.H"
+#include <FL/Fl_Plugin.H>
+#include "Fl_Wayland_Screen_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;
+
+struct Fl_Window_Driver::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_;
+};
+
+
+class FL_EXPORT Fl_Wayland_Window_Driver : public Fl_Window_Driver
+{
+ friend class Fl_X;
+private:
+ static bool in_flush; // useful for progressive window drawing
+ struct wl_cursor *cursor_;
+ void delete_cursor_();
+public:
+ struct wl_cursor *cursor() { return cursor_; };
+ bool in_handle_configure; // distinguish OS and user window resize
+ struct window_output {
+ Fl_Wayland_Screen_Driver::output* output;
+ struct wl_list link;
+ };
+
+ struct icon_data {
+ const void *legacy_icon;
+ Fl_RGB_Image **icons;
+ int count;
+ } *icon_;
+ // --- support for screen-specific scaling factors
+ struct type_for_resize_window_between_screens {
+ int screen;
+ bool busy;
+ };
+ static type_for_resize_window_between_screens data_for_resize_window_between_screens_;
+ int screen_num_;
+ void screen_num(int n) { screen_num_ = n; }
+ void decorated_win_size(int &w, int &h);
+ void shape_bitmap_(Fl_Image* b);
+ void shape_alpha_(Fl_Image* img, int offset);
+ void update_scale();
+
+public:
+ enum kind {DECORATED, SUBWINDOW, POPUP, UNFRAMED};
+ struct xdg_toplevel *xdg_toplevel();
+ Fl_Wayland_Window_Driver(Fl_Window*);
+ virtual ~Fl_Wayland_Window_Driver();
+ static void redraw(struct wld_window *window);
+
+ static inline Fl_Wayland_Window_Driver* driver(const Fl_Window *w) {return (Fl_Wayland_Window_Driver*)Fl_Window_Driver::driver(w);}
+ virtual int screen_num();
+ static void resize_after_screen_change(void *data);
+
+ // --- window data
+ virtual int decorated_w();
+ virtual int decorated_h();
+ virtual const Fl_Image* shape();
+
+ // --- window management
+ virtual Fl_X *makeWindow();
+ virtual void take_focus();
+ virtual void flush();
+ virtual void flush_overlay();
+ virtual void draw_end();
+ virtual void make_current();
+ virtual void show();
+ virtual void resize(int X,int Y,int W,int H);
+ virtual void label(const char *name, const char *mininame);
+ virtual void destroy_double_buffer();
+ virtual void hide();
+ virtual void map();
+ virtual void unmap();
+ virtual void fullscreen_on();
+ virtual void fullscreen_off(int X, int Y, int W, int H);
+ virtual void use_border();
+ virtual void size_range();
+ virtual void iconize();
+ virtual void decoration_sizes(int *top, int *left, int *right, int *bottom);
+ virtual void show_with_args_begin();
+ virtual void show_with_args_end(int argc, char **argv);
+ // --- window cursor stuff
+ virtual int set_cursor(Fl_Cursor);
+ virtual int set_cursor(const Fl_RGB_Image*, int, int);
+
+ virtual void shape(const Fl_Image* img);
+ virtual void icons(const Fl_RGB_Image *icons[], int count);
+ virtual const void *icon() const;
+ virtual void icon(const void * ic);
+ virtual void free_icons();
+ virtual void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right);
+ virtual 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);
+ virtual void wait_for_expose();
+ virtual void reposition_menu_window(int x, int y);
+ virtual void menu_window_area(int &X, int &Y, int &W, int &H, int nscreen = -1);
+};
+
+
+struct wld_window {
+ struct wl_list outputs;
+ struct wl_surface *wl_surface;
+ struct fl_wld_buffer *buffer;
+ struct xdg_surface *xdg_surface;
+ union {
+ struct libdecor_frame *frame;
+ struct wl_subsurface *subsurface;
+ struct xdg_popup *xdg_popup;
+ struct xdg_toplevel *xdg_toplevel;
+ };
+ Fl_Window *fl_win;
+ enum Fl_Wayland_Window_Driver::kind kind;
+ int configured_width;
+ int configured_height;
+ int floating_width;
+ int floating_height;
+ int scale;
+ int state;
+};
+
+
+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;
+};
+
+#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
new file mode 100644
index 000000000..9738ab861
--- /dev/null
+++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx
@@ -0,0 +1,1582 @@
+//
+// Implementation of the Wayland window driver.
+//
+// Copyright 1998-2022 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#include <FL/platform.H>
+#include "Fl_Wayland_Window_Driver.H"
+#include "Fl_Wayland_Screen_Driver.H"
+#include "Fl_Wayland_Graphics_Driver.H"
+#include "Fl_Wayland_System_Driver.H"
+#include <wayland-cursor.h>
+#include "../../../libdecor/src/libdecor.h"
+#include "xdg-shell-client-protocol.h"
+#include <pango/pangocairo.h>
+#include <FL/Fl_Overlay_Window.H>
+#include <FL/Fl_Menu_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 <string.h>
+#include <sys/mman.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))
+
+Window fl_window;
+
+
+void Fl_Wayland_Window_Driver::destroy_double_buffer() {
+ if (pWindow->as_overlay_window()) fl_delete_offscreen(other_xid);
+ other_xid = 0;
+}
+
+
+Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w)
+{
+ return new Fl_Wayland_Window_Driver(w);
+}
+
+
+Fl_Wayland_Window_Driver::Fl_Wayland_Window_Driver(Fl_Window *win) : Fl_Window_Driver(win)
+{
+ icon_ = new icon_data;
+ memset(icon_, 0, sizeof(icon_data));
+ cursor_ = NULL;
+ in_handle_configure = false;
+ screen_num_ = -1;
+}
+
+void Fl_Wayland_Window_Driver::delete_cursor_() {
+ if (cursor_) {
+ struct cursor_image *new_image = (struct cursor_image*)cursor_->images[0];
+ struct fl_wld_buffer *offscreen = (struct fl_wld_buffer *)wl_buffer_get_user_data(new_image->buffer);
+ struct wld_window fake_xid;
+ fake_xid.buffer = offscreen;
+ Fl_Wayland_Graphics_Driver::buffer_release(&fake_xid);
+ free(new_image);
+ free(cursor_->images);
+ free(cursor_->name);
+ free(cursor_);
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ if (scr_driver->default_cursor() == cursor_) scr_driver->default_cursor(scr_driver->xc_arrow);
+ cursor_ = NULL;
+ }
+}
+
+
+Fl_Wayland_Window_Driver::~Fl_Wayland_Window_Driver()
+{
+ if (shape_data_) {
+ cairo_surface_t *surface;
+ cairo_pattern_get_surface(shape_data_->mask_pattern_, &surface);
+ cairo_pattern_destroy(shape_data_->mask_pattern_);
+ uchar *data = cairo_image_surface_get_data(surface);
+ cairo_surface_destroy(surface);
+ delete[] data;
+ delete shape_data_;
+ }
+ delete icon_;
+ delete_cursor_();
+}
+
+
+// --- private
+
+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_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()));
+}
+
+
+// --- window data
+
+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() {
+ Window w = fl_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()
+{
+ Window w = fl_xid(pWindow);
+ if (w) {
+ Fl_Window *old_first = Fl::first_window();
+ Window first_xid = (old_first ? fl_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
+ old_first->wait_for_expose();
+ xdg_toplevel_set_parent(xdg_toplevel(), NULL);
+ }
+ // this sets the first window
+ fl_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 = fl_create_offscreen(oWindow->w(), oWindow->h());
+ oWindow->clear_damage(FL_DAMAGE_ALL);
+ }
+ if (oWindow->damage() & ~FL_DAMAGE_EXPOSE) {
+ Fl_X *myi = Fl_X::i(pWindow);
+ fl_clip_region(myi->region); myi->region = 0;
+ fl_begin_offscreen(other_xid);
+ draw();
+ fl_end_offscreen();
+ }
+ if (erase_overlay) fl_clip_region(0);
+ if (other_xid) {
+ fl_copy_offscreen(0, 0, oWindow->w(), oWindow->h(), other_xid, 0, 0);
+ }
+ if (overlay() == oWindow) oWindow->draw_overlay();
+ Window xid = fl_xid(pWindow);
+ wl_surface_damage_buffer(xid->wl_surface, 0, 0, pWindow->w() * xid->scale, pWindow->h() * xid->scale);
+}
+
+
+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) { // needs testing
+ // complement the bits of the Fl_Bitmap and control its stride too
+ int i, j, w = b->w(), h = b->h();
+ int bytesperrow = cairo_format_stride_for_width(CAIRO_FORMAT_A1, w);
+ uchar* bits = new uchar[h * bytesperrow];
+ const uchar *q = ((Fl_Bitmap*)b)->array;
+ for (i = 0; i < h; i++) {
+ uchar *p = bits + i * bytesperrow;
+ for (j = 0; j < w; j++) {
+ *p++ = ~*q++;
+ }
+ }
+ 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);
+ shape_data_->shape_ = b;
+ shape_data_->lw_ = w;
+ shape_data_->lh_ = h;
+}
+
+void Fl_Wayland_Window_Driver::shape_alpha_(Fl_Image* img, int offset) {
+ int i, j, d = img->d(), w = img->w(), h = img->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 rgba 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);
+ 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);
+ cairo_pattern_destroy(shape_data_->mask_pattern_);
+ uchar *data = cairo_image_surface_get_data(surface);
+ cairo_surface_destroy(surface);
+ 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() , double(shape_data_->lh_)/pWindow->h());
+ 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);
+ }
+}
+
+
+void Fl_Wayland_Window_Driver::icons(const Fl_RGB_Image *icons[], int count) {
+ free_icons();
+
+ if (count > 0) {
+ icon_->icons = new Fl_RGB_Image*[count];
+ icon_->count = count;
+ // FIXME: Fl_RGB_Image lacks const modifiers on methods
+ for (int i = 0;i < count;i++) {
+ icon_->icons[i] = (Fl_RGB_Image*)((Fl_RGB_Image*)icons[i])->copy();
+ icon_->icons[i]->normalize();
+ }
+ }
+}
+
+const void *Fl_Wayland_Window_Driver::icon() const {
+ return icon_->legacy_icon;
+}
+
+void Fl_Wayland_Window_Driver::icon(const void * ic) {
+ free_icons();
+ icon_->legacy_icon = ic;
+}
+
+void Fl_Wayland_Window_Driver::free_icons() {
+ int i;
+ icon_->legacy_icon = 0L;
+ if (icon_->icons) {
+ for (i = 0;i < icon_->count;i++)
+ delete icon_->icons[i];
+ delete [] icon_->icons;
+ icon_->icons = 0L;
+ }
+ icon_->count = 0;
+}
+
+
+/* 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_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 * 3];
+ 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
+ q += 4;
+ }
+ }
+ top = new Fl_RGB_Image(data, width, height, 3);
+ top->alloc_array = 1;
+ top->scale(pWindow->w(), htop);
+}
+
+// used only to support progressive drawing
+static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time);
+
+static const struct wl_callback_listener surface_frame_listener = {
+ .done = surface_frame_done,
+};
+
+static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
+ Window window = (Window)data;
+//fprintf(stderr,"surface_frame_done: destroy cb=%p draw_buffer_needs_commit=%d\n", cb, window->buffer->draw_buffer_needs_commit);
+ wl_callback_destroy(cb);
+ window->buffer->cb = NULL;
+ if (window->buffer->draw_buffer_needs_commit) {
+ wl_surface_damage_buffer(window->wl_surface, 0, 0, 1000000, 1000000);
+ window->buffer->cb = wl_surface_frame(window->wl_surface);
+//fprintf(stderr,"surface_frame_done: new cb=%p \n", window->buffer->cb);
+ wl_callback_add_listener(window->buffer->cb, &surface_frame_listener, window);
+ Fl_Wayland_Graphics_Driver::buffer_commit(window);
+ }
+}
+
+
+// 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_xid(pWindow);
+ float scale = Fl::screen_scale(pWindow->screen_num()) * window->scale;
+
+ // to support progressive drawing
+ if ( (!Fl_Wayland_Window_Driver::in_flush) && window && window->buffer && (!window->buffer->cb)) {
+ wl_surface_damage_buffer(window->wl_surface, 0, 0, pWindow->w() * scale, pWindow->h() * scale);
+ window->buffer->cb = wl_surface_frame(window->wl_surface);
+ //fprintf(stderr, "direct make_current: new cb=%p\n", window->buffer->cb);
+ wl_callback_add_listener(window->buffer->cb, &surface_frame_listener, window);
+ Fl_Wayland_Graphics_Driver::buffer_commit(window);
+ }
+
+ fl_graphics_driver->clip_region(0);
+ fl_window = window;
+ if (!window->buffer) window->buffer = Fl_Wayland_Graphics_Driver::create_shm_buffer(
+ pWindow->w() * scale, pWindow->h() * scale);
+ ((Fl_Wayland_Graphics_Driver*)fl_graphics_driver)->activate(window->buffer, scale);
+
+#ifdef FLTK_USE_CAIRO
+ // 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;
+ 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");
+ }
+ if (plugin) {
+ plugin->do_swap(pWindow); // useful only for GL win with overlay
+ if (scale != fl_graphics_driver->scale() || W != pWindow->w() || H != pWindow->h()) plugin->invalidate(pWindow);
+ }
+ return;
+ }
+ struct wld_window *window = fl_xid(pWindow);
+ if (!window || !window->configured_width) return;
+
+ Fl_X *i = Fl_X::i(pWindow);
+ Fl_Region r = i->region;
+ float f = Fl::screen_scale(pWindow->screen_num());
+ if (r && window->buffer) {
+ for (int i = 0; i < r->count; i++) {
+ int left = r->rects[i].x * window->scale * f;
+ int top = r->rects[i].y * window->scale * f;
+ int width = r->rects[i].width * window->scale * f;
+ int height = r->rects[i].height * window->scale * f;
+ wl_surface_damage_buffer(window->wl_surface, left, top, width, height);
+//fprintf(stderr, "damage %dx%d %dx%d\n", left, top, width, height);
+ }
+ } else {
+ wl_surface_damage_buffer(window->wl_surface, 0, 0,
+ pWindow->w() * window->scale * f, pWindow->h() * window->scale * f);
+//fprintf(stderr, "damage 0x0 %dx%d\n", pWindow->w() * window->scale, pWindow->h() * window->scale);
+ }
+
+ Fl_Wayland_Window_Driver::in_flush = true;
+ Fl_Window_Driver::flush();
+ Fl_Wayland_Window_Driver::in_flush = false;
+
+ wl_surface_frame(window->wl_surface);
+ Fl_Wayland_Graphics_Driver::buffer_commit(window);
+}
+
+
+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 delayed_delete_Fl_X(Fl_X *i) {
+ delete i;
+}
+
+
+void Fl_Wayland_Window_Driver::hide() {
+ Fl_X* ip = Fl_X::i(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 = ip->xid;
+ if (wld_win) { // this test makes sure ip->xid has not been destroyed already
+ Fl_Wayland_Graphics_Driver::buffer_release(wld_win);
+//fprintf(stderr, "Before hide: sub=%p frame=%p xdg=%p top=%p pop=%p surf=%p\n", wld_win->subsurface, wld_win->frame, wld_win->xdg_surface, wld_win->xdg_toplevel, wld_win->xdg_popup, wld_win->wl_surface);
+ if (wld_win->kind == SUBWINDOW && wld_win->subsurface) {
+ wl_subsurface_destroy(wld_win->subsurface);
+ wld_win->subsurface = NULL;
+ }
+ 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) {
+ popup_done(wld_win, 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->wl_surface) {
+ wl_surface_destroy(wld_win->wl_surface);
+ wld_win->wl_surface = NULL;
+ }
+ Fl_Wayland_Window_Driver::window_output *window_output, *tmp;
+ wl_list_for_each_safe(window_output, tmp, &wld_win->outputs, link) {
+ wl_list_remove(&window_output->link);
+ free(window_output);
+ }
+//fprintf(stderr, "After hide: sub=%p frame=%p xdg=%p top=%p pop=%p surf=%p\n", wld_win->subsurface, wld_win->frame, wld_win->xdg_surface, wld_win->xdg_toplevel, wld_win->xdg_popup, wld_win->wl_surface);
+ }
+ free(wld_win);
+ if (pWindow->as_gl_window() && in_flush) {
+ ip->xid = NULL;
+ ip->next = NULL; // to end the loop in calling Fl::flush()
+ Fl::add_timeout(.01, (Fl_Timeout_Handler)delayed_delete_Fl_X, ip);
+ } else {
+ delete ip;
+ }
+}
+
+
+void Fl_Wayland_Window_Driver::map() {
+ Fl_X* ip = Fl_X::i(pWindow);
+ struct wld_window *wl_win = ip->xid;
+ if (wl_win->kind == DECORATED) libdecor_frame_map(wl_win->frame);//needs checking
+ else if (pWindow->parent() && !wl_win->subsurface) {
+ struct wld_window *parent = fl_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();
+ wl_win->scale = parent->scale;
+ wait_for_expose_value = 0;
+ pWindow->redraw();
+ }
+ }
+}
+
+
+void Fl_Wayland_Window_Driver::unmap() {
+ Fl_X* ip = Fl_X::i(pWindow);
+ struct wld_window *wl_win = ip->xid;
+ if (wl_win->kind == DECORATED && wl_win->frame) { libdecor_frame_close(wl_win->frame);//needs checking
+ } else 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::i(pWindow);
+ struct wld_window *wl_win = ip->xid;
+ float f = Fl::screen_scale(pWindow->screen_num());
+ if (wl_win->kind == DECORATED && wl_win->frame) {
+ 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);
+ 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);
+ }
+ } 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::i(pWindow);
+ struct wld_window *wl_win = ip->xid;
+ if (wl_win->kind == DECORATED) {
+ libdecor_frame_set_minimized(wl_win->frame);
+ 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) {
+ // Ensure border is on screen; these values are generic enough
+ // to work with many window managers, and are based on KDE defaults.
+ *top = 20;
+ *left = 4;
+ *right = 4;
+ *bottom = 8;
+}
+
+void Fl_Wayland_Window_Driver::show_with_args_begin() {
+ // Get defaults for drag-n-drop and focus...
+ const char *key = 0;
+
+ if (Fl::first_window()) key = Fl::first_window()->xclass();
+ if (!key) key = "fltk";
+
+ /*const char *val = XGetDefault(fl_display, key, "dndTextOps");
+ if (val) Fl::dnd_text_ops(strcasecmp(val, "true") == 0 ||
+ strcasecmp(val, "on") == 0 ||
+ strcasecmp(val, "yes") == 0);
+
+ val = XGetDefault(fl_display, key, "tooltips");
+ if (val) Fl_Tooltip::enable(strcasecmp(val, "true") == 0 ||
+ strcasecmp(val, "on") == 0 ||
+ strcasecmp(val, "yes") == 0);
+
+ val = XGetDefault(fl_display, key, "visibleFocus");
+ if (val) Fl::visible_focus(strcasecmp(val, "true") == 0 ||
+ strcasecmp(val, "on") == 0 ||
+ strcasecmp(val, "yes") == 0);*/
+}
+
+
+void Fl_Wayland_Window_Driver::show_with_args_end(int argc, char **argv) {
+ if (argc) {
+ // set the command string, used by state-saving window managers:
+ int j;
+ int n=0; for (j=0; j<argc; j++) n += strlen(argv[j])+1;
+ char *buffer = new char[n];
+ char *p = buffer;
+ for (j=0; j<argc; j++) for (const char *q = argv[j]; (*p++ = *q++););
+ //XChangeProperty(fl_display, fl_xid(pWindow), XA_WM_COMMAND, XA_STRING, 8, 0,
+ // (unsigned char *)buffer, p-buffer-1);
+ delete[] buffer;
+ }
+}
+
+
+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)
+{
+ Window xid = fl_xid(pWindow);
+ struct fl_wld_buffer *buffer = xid->buffer;
+ int s = xid->scale;
+ if (s != 1) {
+ src_x *= s; src_y *= s; src_w *= s; src_h *= s; dest_x *= s; 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 + (dest_y + i) * buffer->stride + 4 * dest_x,
+ buffer->draw_buffer + (src_y + i) * 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 + (src_y + i) * buffer->stride + 4 * dest_x,
+ buffer->draw_buffer + (src_y + i) * 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 surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output)
+{
+ struct wld_window *window = (struct wld_window*)data;
+ Fl_Wayland_Window_Driver::window_output *window_output;
+
+ 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;
+
+ window_output = (Fl_Wayland_Window_Driver::window_output*)calloc(1, sizeof *window_output);
+ window_output->output = output;
+ wl_list_insert(&window->outputs, &window_output->link);
+ Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
+ win_driver->update_scale();
+ if (!window->fl_win->parent()) { // for top-level, set its screen number
+ 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);
+//fprintf(stderr,"window %p is on screen #%d\n", window->fl_win, i);
+ break;
+ }
+ i++;
+ }
+ }
+}
+
+static void surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output)
+{
+ struct wld_window *window = (struct wld_window*)data;
+ if (! window->wl_surface) return;
+ Fl_Wayland_Window_Driver::window_output *window_output;
+
+ wl_list_for_each(window_output, &window->outputs, link) {
+ if (window_output->output->wl_output == wl_output) {
+ wl_list_remove(&window_output->link);
+ free(window_output);
+ Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
+ win_driver->update_scale();
+ break;
+ }
+ }
+}
+
+static struct wl_surface_listener surface_listener = {
+ surface_enter,
+ surface_leave,
+};
+
+
+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 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);
+
+ if (window->fl_win->fullscreen_active()) {
+ libdecor_frame_set_fullscreen(window->frame, NULL);
+ } 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;
+ window->state = window_state;
+
+ // Weston, KDE and recent 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.
+ // In that case libdecor_frame_get_content_{width,height}() give the desired window size
+ if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
+ if (is_2nd_run) {
+ width = libdecor_frame_get_content_width(frame);
+ height = libdecor_frame_get_content_height(frame);
+ if (!window->fl_win->resizable()) {
+ libdecor_frame_set_min_content_size(frame, width, height);
+ libdecor_frame_set_max_content_size(frame, width, height);
+ }
+ } else { width = height = 0; }
+ }
+
+ if (width == 0) {
+ width = window->floating_width;
+ height = window->floating_height;
+ //fprintf(stderr,"handle_configure: using floating %dx%d\n",width,height);
+ }
+
+ driver->in_handle_configure = true;
+ window->fl_win->resize(0, 0, ceil(width / f), ceil(height / f));
+ driver->in_handle_configure = false;
+
+ if (ceil(width / f) != window->configured_width || ceil(height / f) != window->configured_height) {
+ if (window->buffer) {
+ Fl_Wayland_Graphics_Driver::buffer_release(window);
+ }
+ }
+ 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);
+
+/* We would like to do FL_HIDE when window is minimized but :
+ "There is no way to know if the surface is currently minimized, nor is there any way to
+ unset minimization on this surface. If you are looking to throttle redrawing when minimized,
+ please instead use the wl_surface.frame event" */
+ if (window_state == LIBDECOR_WINDOW_STATE_NONE) {
+ Fl::handle(FL_UNFOCUS, window->fl_win);
+ }
+ else if (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) {
+ 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);
+ Fl::handle(FL_FOCUS, window->fl_win);
+ fl_find(window);
+ }
+
+ 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);
+
+ window->fl_win->redraw();
+ // necessary with SSD
+ driver->in_handle_configure = true;
+ if (!window->fl_win->as_gl_window()) {
+ driver->flush();
+ } else {
+ driver->Fl_Window_Driver::flush(); // GL window
+ }
+ driver->in_handle_configure = false;
+}
+
+
+void Fl_Wayland_Window_Driver::wait_for_expose()
+{
+ Fl_Window_Driver::wait_for_expose();
+ if (pWindow->fullscreen_active()) {
+ Window xid = fl_xid(pWindow);
+ if (xid->kind == DECORATED) {
+ while (!(xid->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) || !(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) {
+ wl_display_dispatch(fl_display);
+ }
+ } else if (xid->kind == UNFRAMED) {
+ wl_display_roundtrip(fl_display);
+ }
+ }
+}
+
+
+static void handle_close(struct libdecor_frame *frame, void *user_data)
+{
+ struct wld_window* wl_win = (struct wld_window*)user_data;
+ Fl::handle(FL_CLOSE, wl_win->fl_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();
+ window->fl_win->redraw();
+ Fl_Window_Driver::driver(window->fl_win)->flush();
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_configure,
+};
+
+
+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
+ 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()) xdg_toplevel_set_fullscreen(xdg_toplevel, NULL);
+ 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,
+};
+
+
+static void popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) {
+ struct wld_window *window = (struct wld_window*)data;
+ Fl_Window_Driver::driver(window->fl_win)->wait_for_expose_value = 0;
+}
+
+#define USE_GRAB_POPUP 0
+#if USE_GRAB_POPUP // nearly OK except that menutitle window does not always show
+static Fl_Window *mem_parent = NULL;
+static struct xdg_popup *mem_grabbing_popup = NULL;
+
+static void nothing_popup(void *, struct xdg_popup *) {}
+#endif
+
+static void popup_done(void *data, struct xdg_popup *xdg_popup) {
+//fprintf(stderr, "popup_done: popup=%p \n", xdg_popup);
+#if USE_GRAB_POPUP
+ if (mem_grabbing_popup == xdg_popup) {
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ libdecor_frame_popup_ungrab(fl_xid(mem_parent)->frame, scr_driver->get_seat_name());
+ mem_grabbing_popup = NULL;
+ mem_parent = NULL;
+ }
+#endif
+ xdg_popup_destroy(xdg_popup);
+}
+
+static const struct xdg_popup_listener popup_listener = {
+ .configure = popup_configure,
+#if USE_GRAB_POPUP
+ .popup_done = nothing_popup,
+#else
+ .popup_done = popup_done,
+#endif
+};
+
+bool Fl_Wayland_Window_Driver::in_flush = false;
+
+// Compute the parent window of the transient scale window
+static Fl_Window *calc_transient_parent(int &center_x, int &center_y) {
+ // Find top, the topmost window, but not a transient window itself
+ Fl_Window *top = Fl::first_window()->top_window();
+ while (top && top->user_data() == &Fl_Screen_Driver::transient_scale_display)
+ top = Fl::next_window(top);
+ Fl_Window *target = top;
+ // search if top's center belongs to one of its subwindows
+ center_x = top->w()/2; center_y = top->h()/2;
+ while (target) {
+ Fl_Window *child = Fl::first_window();
+ while (child) {
+ if (child->window() == target && child->user_data() != &Fl_Screen_Driver::transient_scale_display &&
+ child->x() <= center_x && child->x()+child->w() > center_x &&
+ child->y() <= center_y && child->y()+child->h() > center_y) {
+ break; // child contains the center of top
+ }
+ child = Fl::next_window(child);
+ }
+ if (!child) break; // no more subwindow contains the center of top
+ target = child;
+ center_x -= child->x(); // express center coordinates relatively to child
+ center_y -= child->y();
+ }
+ return target;
+}
+
+
+static char *get_prog_name() {
+ pid_t pid = getpid();
+ char fname[100];
+ sprintf(fname, "/proc/%u/cmdline", pid);
+ FILE *in = fopen(fname, "r");
+ if (in) {
+ static char line[200];
+ char *p = fgets(line, sizeof(line), in);
+ fclose(in);
+ p = strrchr(line, '/'); if (!p) p = line; else p++;
+ return p;
+ }
+ return NULL;
+}
+
+
+Fl_X *Fl_Wayland_Window_Driver::makeWindow()
+{
+ struct wld_window *new_window;
+ Fl_Wayland_Screen_Driver::output *output;
+ wait_for_expose_value = 1;
+
+ if (pWindow->parent() && !pWindow->window()->shown()) return NULL;
+
+ new_window = (struct wld_window *)calloc(1, sizeof *new_window);
+ new_window->fl_win = pWindow;
+ new_window->scale = 1;
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ wl_list_for_each(output, &(scr_driver->outputs), link) {
+ new_window->scale = MAX(new_window->scale, output->wld_scale);
+ }
+ wl_list_init(&new_window->outputs);
+
+ new_window->wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
+ //Fl::warning("makeWindow:%p wayland-scale=%d user-scale=%.2f\n", pWindow, new_window->scale, Fl::screen_scale(0));
+ wl_surface_add_listener(new_window->wl_surface, &surface_listener, new_window);
+
+ 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 child of top
+ int center_x, center_y;
+ Fl_Window *top = calc_transient_parent(center_x, center_y);
+ if (top) {
+ top->add(pWindow);
+ pWindow->position(center_x - pWindow->w()/2 , center_y - pWindow->h()/2);
+ }
+ }
+
+ if (pWindow->menu_window() || pWindow->tooltip_window()) { // a menu window or tooltip
+ new_window->kind = POPUP;
+ 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);
+ struct xdg_positioner *positioner = xdg_wm_base_create_positioner(scr_driver->xdg_wm_base);
+ //xdg_positioner_get_version(positioner) <== gives 1 under Debian
+ Fl_Widget *target = (pWindow->tooltip_window() ?
+ Fl_Tooltip::current() : 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()) parent_win = Fl::next_window(parent_win);
+ Window parent_xid = fl_xid(parent_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());
+ int popup_x = pWindow->x() * f, popup_y = pWindow->y() * f;
+ if (parent_xid->kind == DECORATED)
+ libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, &popup_x, &popup_y);
+ xdg_positioner_set_anchor_rect(positioner, popup_x, popup_y, 1, 1);
+ xdg_positioner_set_size(positioner, pWindow->w() * f , pWindow->h() * f );
+ xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
+ xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
+ new_window->xdg_popup = xdg_surface_get_popup(new_window->xdg_surface, parent_xdg, positioner);
+ xdg_positioner_destroy(positioner);
+ xdg_popup_add_listener(new_window->xdg_popup, &popup_listener, new_window);
+#if USE_GRAB_POPUP
+ if (!mem_grabbing_popup) {
+ mem_parent = parent_win;
+ 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());
+ }
+#endif
+ wl_surface_commit(new_window->wl_surface);
+
+ } 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_display, &libdecor_iface);
+ new_window->frame = libdecor_decorate(scr_driver->libdecor_context, new_window->wl_surface,
+ &libdecor_frame_iface, new_window);
+//fprintf(stderr, "makeWindow: libdecor_decorate=%p pos:%dx%d\n", new_window->frame, pWindow->x(), pWindow->y());
+ libdecor_frame_set_app_id(new_window->frame, get_prog_name()); // appears in the Gnome desktop menu bar
+ libdecor_frame_set_title(new_window->frame, pWindow->label()?pWindow->label():"");
+ if (!pWindow->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_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
+ wl_subsurface_place_above(new_window->subsurface, parent->wl_surface);
+ // next 3 statements ensure the subsurface will be mapped because:
+ // "A sub-surface becomes mapped, when a non-NULL wl_buffer is applied and the parent surface is mapped."
+ new_window->configured_width = pWindow->w();
+ new_window->configured_height = pWindow->h();
+ wait_for_expose_value = 0;
+ pWindow->border(0);
+
+ } 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();
+ Window first_xid = (old_first ? fl_xid(old_first) : NULL);
+ Fl_X *xp = new Fl_X;
+ xp->xid = new_window;
+ other_xid = 0;
+ xp->w = pWindow;
+ i(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) {
+ 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());
+ }
+ }
+
+ size_range();
+ pWindow->set_visible();
+ int old_event = Fl::e_number;
+ pWindow->handle(Fl::e_number = FL_SHOW); // get child windows to appear
+ Fl::e_number = old_event;
+ pWindow->redraw();
+
+ return xp;
+}
+
+Fl_Wayland_Window_Driver::type_for_resize_window_between_screens Fl_Wayland_Window_Driver::data_for_resize_window_between_screens_ = {0, false};
+
+void Fl_Wayland_Window_Driver::resize_after_screen_change(void *data) {
+ Fl_Window *win = (Fl_Window*)data;
+ float f = Fl::screen_driver()->scale(data_for_resize_window_between_screens_.screen);
+ Fl_Window_Driver::driver(win)->resize_after_scale_change(data_for_resize_window_between_screens_.screen, f, f);
+ data_for_resize_window_between_screens_.busy = false;
+}
+
+
+int Fl_Wayland_Window_Driver::screen_num() {
+ if (pWindow->parent()) {
+ screen_num_ = Fl_Window_Driver::driver(pWindow->top_window())->screen_num();
+ }
+ return screen_num_ >= 0 ? screen_num_ : 0;
+}
+
+
+int Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor c) {
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+
+ // Cursor names are the files of directory /usr/share/icons/XXXX/cursors/
+ // where XXXX is the name of the current 'cursor theme'.
+ switch (c) {
+ case FL_CURSOR_ARROW:
+ if (!scr_driver->xc_arrow) scr_driver->xc_arrow = scr_driver->cache_cursor("left_ptr");
+ scr_driver->default_cursor(scr_driver->xc_arrow);
+ break;
+ case FL_CURSOR_NS:
+ if (!scr_driver->xc_ns) scr_driver->xc_ns = scr_driver->cache_cursor("sb_v_double_arrow");
+ if (!scr_driver->xc_ns) return 0;
+ scr_driver->default_cursor(scr_driver->xc_ns);
+ break;
+ case FL_CURSOR_CROSS:
+ if (!scr_driver->xc_cross) scr_driver->xc_cross = scr_driver->cache_cursor("cross");
+ if (!scr_driver->xc_cross) return 0;
+ scr_driver->default_cursor(scr_driver->xc_cross);
+ break;
+ case FL_CURSOR_WAIT:
+ if (!scr_driver->xc_wait) scr_driver->xc_wait = scr_driver->cache_cursor("wait");
+ if (!scr_driver->xc_wait) scr_driver->xc_wait = scr_driver->cache_cursor("watch");
+ if (!scr_driver->xc_wait) return 0;
+ scr_driver->default_cursor(scr_driver->xc_wait);
+ break;
+ case FL_CURSOR_INSERT:
+ if (!scr_driver->xc_insert) scr_driver->xc_insert = scr_driver->cache_cursor("xterm");
+ if (!scr_driver->xc_insert) return 0;
+ scr_driver->default_cursor(scr_driver->xc_insert);
+ break;
+ case FL_CURSOR_HAND:
+ if (!scr_driver->xc_hand) scr_driver->xc_hand = scr_driver->cache_cursor("hand");
+ if (!scr_driver->xc_hand) scr_driver->xc_hand = scr_driver->cache_cursor("hand1");
+ if (!scr_driver->xc_hand) return 0;
+ scr_driver->default_cursor(scr_driver->xc_hand);
+ break;
+ case FL_CURSOR_HELP:
+ if (!scr_driver->xc_help) scr_driver->xc_help = scr_driver->cache_cursor("help");
+ if (!scr_driver->xc_help) return 0;
+ scr_driver->default_cursor(scr_driver->xc_help);
+ break;
+ case FL_CURSOR_MOVE:
+ if (!scr_driver->xc_move) scr_driver->xc_move = scr_driver->cache_cursor("move");
+ if (!scr_driver->xc_move) return 0;
+ scr_driver->default_cursor(scr_driver->xc_move);
+ break;
+ case FL_CURSOR_WE:
+ if (!scr_driver->xc_we) scr_driver->xc_we = scr_driver->cache_cursor("sb_h_double_arrow");
+ if (!scr_driver->xc_we) return 0;
+ scr_driver->default_cursor(scr_driver->xc_we);
+ break;
+ case FL_CURSOR_N:
+ if (!scr_driver->xc_north) scr_driver->xc_north = scr_driver->cache_cursor("top_side");
+ if (!scr_driver->xc_north) return 0;
+ scr_driver->default_cursor(scr_driver->xc_north);
+ break;
+ case FL_CURSOR_E:
+ if (!scr_driver->xc_east) scr_driver->xc_east = scr_driver->cache_cursor("right_side");
+ if (!scr_driver->xc_east) return 0;
+ scr_driver->default_cursor(scr_driver->xc_east);
+ break;
+ case FL_CURSOR_W:
+ if (!scr_driver->xc_west) scr_driver->xc_west = scr_driver->cache_cursor("left_side");
+ if (!scr_driver->xc_west) return 0;
+ scr_driver->default_cursor(scr_driver->xc_west);
+ break;
+ case FL_CURSOR_S:
+ if (!scr_driver->xc_south) scr_driver->xc_south = scr_driver->cache_cursor("bottom_side");
+ if (!scr_driver->xc_south) return 0;
+ scr_driver->default_cursor(scr_driver->xc_south);
+ break;
+ case FL_CURSOR_NESW:
+ if (!scr_driver->xc_nesw) scr_driver->xc_nesw = scr_driver->cache_cursor("fd_double_arrow");
+ if (!scr_driver->xc_nesw) return 0;
+ scr_driver->default_cursor(scr_driver->xc_nesw);
+ break;
+ case FL_CURSOR_NWSE:
+ if (!scr_driver->xc_nwse) scr_driver->xc_nwse = scr_driver->cache_cursor("bd_double_arrow");
+ if (!scr_driver->xc_nwse) return 0;
+ scr_driver->default_cursor(scr_driver->xc_nwse);
+ break;
+ case FL_CURSOR_SW:
+ if (!scr_driver->xc_sw) scr_driver->xc_sw = scr_driver->cache_cursor("bottom_left_corner");
+ if (!scr_driver->xc_sw) return 0;
+ scr_driver->default_cursor(scr_driver->xc_sw);
+ break;
+ case FL_CURSOR_SE:
+ if (!scr_driver->xc_se) scr_driver->xc_se = scr_driver->cache_cursor("bottom_right_corner");
+ if (!scr_driver->xc_se) return 0;
+ scr_driver->default_cursor(scr_driver->xc_se);
+ break;
+ case FL_CURSOR_NE:
+ if (!scr_driver->xc_ne) scr_driver->xc_ne = scr_driver->cache_cursor("top_right_corner");
+ if (!scr_driver->xc_ne) return 0;
+ scr_driver->default_cursor(scr_driver->xc_ne);
+ break;
+ case FL_CURSOR_NW:
+ if (!scr_driver->xc_nw) scr_driver->xc_nw = scr_driver->cache_cursor("top_left_corner");
+ if (!scr_driver->xc_nw) return 0;
+ scr_driver->default_cursor(scr_driver->xc_nw);
+ break;
+
+ default:
+ return 0;
+ }
+ if (cursor_) delete_cursor_();
+ scr_driver->set_cursor();
+ return 1;
+}
+
+
+void Fl_Wayland_Window_Driver::update_scale()
+{
+ struct wld_window *window = fl_xid(pWindow);
+ int scale = 0;
+ Fl_Wayland_Window_Driver::window_output *window_output;
+
+ wl_list_for_each(window_output, &window->outputs, link) {
+ scale = fl_max(scale, window_output->output->wld_scale);
+ }
+ if (scale && scale != window->scale) {
+ window->scale = scale;
+ if (window->buffer || window->fl_win->as_gl_window()) {
+ Fl_Wayland_Graphics_Driver::buffer_release(window);
+ window->fl_win->damage(FL_DAMAGE_ALL);
+ Fl_Window_Driver::driver(window->fl_win)->flush();
+ }
+ }
+}
+
+
+void Fl_Wayland_Window_Driver::use_border() {
+ if (!shown() || pWindow->parent()) return;
+ struct libdecor_frame *frame = fl_xid(pWindow)->frame;
+ if (frame && Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::KDE) {
+ libdecor_frame_set_visibility(frame, pWindow->border());
+ 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();
+ wl_display_roundtrip(fl_display); // OK, but try to find something more specific
+ wl_display_roundtrip(fl_display);
+ Fl::handle(FL_FULLSCREEN, pWindow);
+ }
+}
+
+
+void Fl_Wayland_Window_Driver::fullscreen_off(int X, int Y, int W, int H) {
+ if (!border()) pWindow->Fl_Group::resize(X, Y, W, H);
+ xdg_toplevel_unset_fullscreen(xdg_toplevel());
+ pWindow->_clear_fullscreen();
+ Fl::handle(FL_FULLSCREEN, pWindow);
+}
+
+
+void Fl_Wayland_Window_Driver::label(const char *name, const char *iname) {
+ if (shown() && !parent()) {
+ if (!name) name = "";
+ if (!iname) iname = fl_filename_name(name);
+ libdecor_frame_set_title(fl_xid(pWindow)->frame, name);
+ }
+}
+
+
+int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty) {
+// build a new wl_cursor and its image
+ 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 = fl_xid(pWindow)->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_wld_buffer *offscreen = Fl_Wayland_Graphics_Driver::create_shm_buffer(new_image->image.width, new_image->image.height);
+ 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_Image_Surface *img_surf = new Fl_Image_Surface(new_image->image.width, new_image->image.height, 0, offscreen);
+ 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);
+ memset(offscreen->draw_buffer, 0, offscreen->data_size);
+ ((Fl_RGB_Image*)rgb)->draw(0, 0);
+ Fl_Surface_Device::pop_current();
+ delete img_surf;
+ memcpy(offscreen->data, offscreen->draw_buffer, offscreen->data_size);
+ // delete the previous custom cursor, if there was one
+ delete_cursor_();
+ //have this new cursor used
+ cursor_ = new_cursor;
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ scr_driver->default_cursor(cursor_);
+ return 1;
+}
+
+
+#if defined(USE_SYSTEM_LIBDECOR) && USE_SYSTEM_LIBDECOR
+// This is only to fix a bug in libdecor where what libdecor_frame_set_min_content_size()
+// does is often destroyed by libdecor-cairo.
+static void delayed_minsize(Fl_Window *win) {
+ struct wld_window *wl_win = fl_xid(win);
+ Fl_Window_Driver *driver = Fl_Window_Driver::driver(win);
+ if (wl_win->kind == Fl_Wayland_Window_Driver::DECORATED) {
+ float f = Fl::screen_scale(win->screen_num());
+ libdecor_frame_set_min_content_size(wl_win->frame, driver->minw()*f, driver->minh()*f);
+ }
+ bool need_resize = false;
+ int W = win->w(), H = win->h();
+ if (W < driver->minw()) { W = driver->minw(); need_resize = true; }
+ if (H < driver->minh()) { H = driver->minh(); need_resize = true; }
+ if (need_resize) win->size(W, H);
+}
+#endif
+
+
+void Fl_Wayland_Window_Driver::resize(int X, int Y, int W, int H) {
+ int is_a_move = (X != x() || Y != y() || Fl_Window::is_a_rescale());
+ int is_a_resize = (W != w() || H != h() || Fl_Window::is_a_rescale());
+ if (is_a_move) force_position(1);
+ else if (!is_a_resize && !is_a_move) return;
+ if (is_a_resize) {
+ 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 {
+ if (pWindow->parent() || pWindow->menu_window() || pWindow->tooltip_window()) {
+ x(X); y(Y);
+//fprintf(stderr, "move menuwin=%p x()=%d\n", pWindow, X);
+ } else {
+ //"a deliberate design trait of Wayland makes application windows ignorant of their exact placement on screen"
+ x(0); y(0);
+ }
+ }
+
+ if (shown()) {
+ struct wld_window *fl_win = fl_xid(pWindow);
+ float f = Fl::screen_scale(pWindow->screen_num());
+ if (is_a_resize) {
+ 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()) {
+ struct libdecor_state *state = libdecor_state_new(int(W * f), int(H * f));
+ libdecor_frame_commit(fl_win->frame, state, NULL); // necessary only if resize is initiated by prog
+ 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;
+ xdg_surface_set_window_geometry(fl_win->xdg_surface, 0, 0, W * f, H * f);
+ }
+#if defined(USE_SYSTEM_LIBDECOR) && USE_SYSTEM_LIBDECOR
+ if (W < minw() || H < minh()) {
+ Fl::add_timeout(0.01, (Fl_Timeout_Handler)delayed_minsize, pWindow);
+ }
+#endif
+ } else {
+ if (!in_handle_configure && xdg_toplevel()) {
+ // Wayland doesn't seem to provide a reliable 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);
+ } else if (fl_win->kind == SUBWINDOW && fl_win->subsurface) {
+ wl_subsurface_set_position(fl_win->subsurface, pWindow->x() * f, pWindow->y() * f);
+ }
+ }
+ }
+}
+
+void Fl_Wayland_Window_Driver::reposition_menu_window(int x, int y) {
+ Window xid_menu = fl_xid(pWindow);
+ if (y == pWindow->y() && y >= 0) return;
+ int true_y = y;
+ int y_offset = 0;
+ if (y < 0) {
+ y_offset = y-1;
+ y = 1;
+ }
+
+ //printf("should move menuwindow to %d y_offset=%d y=%d\n", true_y, y_offset, pWindow->y());
+ struct xdg_popup *old_popup = xid_menu->xdg_popup;
+ struct xdg_surface *old_xdg = xid_menu->xdg_surface;
+ struct wl_surface *old_surface = xid_menu->wl_surface;
+ // create a new popup at position (x,y) and display it above the current one
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ xid_menu->wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
+ wl_surface_add_listener(xid_menu->wl_surface, &surface_listener, xid_menu);
+ xid_menu->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, xid_menu->wl_surface);
+ xdg_surface_add_listener(xid_menu->xdg_surface, &xdg_surface_listener, xid_menu);
+ struct xdg_positioner *positioner = xdg_wm_base_create_positioner(scr_driver->xdg_wm_base);
+ Window parent_xid = fl_xid(Fl_Window_Driver::menu_parent());
+ float f = Fl::screen_scale(Fl_Window_Driver::menu_parent()->screen_num());
+ int popup_x = x * f, popup_y = y * f;
+ if (parent_xid->kind == DECORATED)
+ libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, &popup_x, &popup_y);
+ xdg_positioner_set_anchor_rect(positioner, popup_x, popup_y, 1, 1);
+ xdg_positioner_set_size(positioner, pWindow->w() * f , pWindow->h() * f );
+ xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
+ xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
+ if (y_offset) xdg_positioner_set_offset(positioner, 0, y_offset);
+ xid_menu->xdg_popup = xdg_surface_get_popup(xid_menu->xdg_surface, parent_xid->xdg_surface, positioner);
+ xdg_positioner_destroy(positioner);
+ xdg_popup_add_listener(xid_menu->xdg_popup, &popup_listener, xid_menu);
+ wl_surface_commit(xid_menu->wl_surface);
+ wl_display_roundtrip(fl_display);
+ // delete the previous popup
+ xdg_popup_destroy(old_popup);
+ xdg_surface_destroy(old_xdg);
+ wl_surface_destroy(old_surface);
+ wl_display_roundtrip(fl_display);
+ this->y(true_y);
+}
+
+
+void Fl_Wayland_Window_Driver::menu_window_area(int &X, int &Y, int &W, int &H, int nscreen) {
+ Fl_Window *parent = Fl_Window_Driver::menu_parent();
+ if (parent) {
+ X = parent->x();
+ Y = parent->y();
+ W = parent->w();
+ H = parent->h();
+ //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);
+}
diff --git a/src/drivers/Wayland/Fl_wayland.cxx b/src/drivers/Wayland/Fl_wayland.cxx
new file mode 100644
index 000000000..f00a7d909
--- /dev/null
+++ b/src/drivers/Wayland/Fl_wayland.cxx
@@ -0,0 +1,672 @@
+//
+// Wayland specific code for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2021 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 <config.h>
+# 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 <stdio.h>
+# include <stdlib.h>
+# include "../../flstring.h"
+# include "Fl_Wayland_Screen_Driver.H"
+# include "Fl_Wayland_Window_Driver.H"
+# include "Fl_Wayland_System_Driver.H"
+# include "Fl_Wayland_Graphics_Driver.H"
+# include <errno.h>
+
+
+////////////////////////////////////////////////////////////////
+
+Window fl_message_window = 0;
+int fl_screen;
+Window fl_xim_win = 0;
+char fl_is_over_the_spot = 0;
+
+
+int Fl_Wayland_Screen_Driver::get_mouse_unscaled(int &mx, int &my) {
+ open_display();
+ mx = Fl::e_x_root; my = Fl::e_y_root;
+ int screen = screen_num_unscaled(mx, my);
+ return screen >= 0 ? screen : 0;
+}
+
+
+int Fl_Wayland_Screen_Driver::get_mouse(int &xx, int &yy) {
+ int snum = get_mouse_unscaled(xx, yy);
+ float s = scale(snum);
+ xx = xx/s;
+ yy = yy/s;
+ return snum;
+}
+
+////////////////////////////////////////////////////////////////
+// Code used for copy and paste and DnD into the program:
+//static Window fl_dnd_source_window;
+
+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;
+static const char *fl_selection_offer_type = 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) == 0 || strcmp(mime_type, "text/plain") == 0 || strcmp(mime_type, "image/bmp") == 0) {
+ 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 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
+//fprintf(stderr, "data_source_handle_cancelled: %p\n", source);
+ wl_data_source_destroy(source);
+ doing_dnd = false;
+ if (dnd_icon) {
+ Fl_Offscreen off = (Fl_Offscreen)wl_surface_get_user_data(dnd_icon);
+ struct wld_window 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) {
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ scr_driver->default_cursor(save_cursor);
+ scr_driver->set_cursor();
+ save_cursor = NULL;
+ }
+ if (fl_dnd_target_window) {
+ Fl::handle(FL_DND_LEAVE, 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 (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 Fl_Offscreen 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;
+ Fl_Offscreen off = Fl_Wayland_Graphics_Driver::create_shm_buffer(width, height);
+ memset(off->draw_buffer, 0, off->data_size);
+ Fl_Image_Surface *surf = new Fl_Image_Surface(width, height, 0, 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->cairo_) );
+ memcpy(off->data, off->draw_buffer, off->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);
+ Fl_Offscreen 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_xid(current->top_window())->scale;
+ off = 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;
+}
+
+
+static void data_offer_handle_offer(void *data, struct wl_data_offer *offer, const char *mime_type) {
+ // runs when app becomes active and lists possible clipboard types
+//fprintf(stderr, "Clipboard offer=%p supports MIME type: %s\n", offer, mime_type);
+ if (strcmp(mime_type, "image/png") == 0) {
+ fl_selection_type[1] = Fl::clipboard_image;
+ fl_selection_offer_type = "image/png";
+ } else if (strcmp(mime_type, "image/bmp") == 0 && (!fl_selection_offer_type || strcmp(fl_selection_offer_type, "image/png"))) {
+ fl_selection_type[1] = Fl::clipboard_image;
+ fl_selection_offer_type = "image/bmp";
+ } else if (strcmp(mime_type, wld_plain_text_clipboard) == 0 && !fl_selection_type[1]) {
+ fl_selection_type[1] = Fl::clipboard_plain_text;
+ }
+}
+
+
+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;
+ fl_selection_offer_type = NULL;
+ wl_data_offer_add_listener(offer, &data_offer_listener, NULL);
+}
+
+
+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");
+}
+
+
+static size_t convert_crlf(char *s, size_t len)
+{ // turn \r characters into \n and "\r\n" sequences into \n:
+ char *p;
+ size_t l = len;
+ while ((p = strchr(s, '\r'))) {
+ if (*(p+1) == '\n') {
+ memmove(p, p+1, l-(p-s));
+ len--; l--;
+ } else *p = '\n';
+ l -= p-s;
+ s = p + 1;
+ }
+ return len;
+}
+
+
+// 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];
+ if (pipe(fds)) return;
+ wl_data_offer_receive(offer, wld_plain_text_clipboard, fds[1]);
+ close(fds[1]);
+ wl_display_flush(fl_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;
+ return;
+ }
+ n = 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)) return;
+ wl_data_offer_receive(offer, wld_plain_text_clipboard, fds[1]);
+ close(fds[1]);
+ wl_display_flush(fl_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;
+ }
+ char *from = fl_selection_buffer[1];
+ while (true) {
+ ssize_t n = read(fds[0], from, rest);
+ if (n <= 0) {
+ close(fds[0]);
+ break;
+ }
+ n = convert_crlf(from, n);
+ from += n;
+ }
+ fl_selection_length[1] = from - fl_selection_buffer[1];;
+ fl_selection_buffer[1][fl_selection_length[1]] = 0;
+ 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_Screen_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) {
+ float f = Fl::screen_scale(win->screen_num());
+ fl_dnd_target_window = win;
+ Fl::e_x = wl_fixed_to_int(x) / f;
+ Fl::e_x_root = Fl::e_x + fl_dnd_target_window->x();
+ Fl::e_y = wl_fixed_to_int(y) / f;
+ 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;
+ }
+ 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::e_x = wl_fixed_to_int(x) / f;
+ Fl::e_x_root = Fl::e_x + fl_dnd_target_window->x();
+ Fl::e_y = wl_fixed_to_int(y) / f;
+ Fl::e_y_root = Fl::e_y + fl_dnd_target_window->y();
+ ret = Fl::handle(FL_DND_DRAG, fl_dnd_target_window);
+ }
+ uint32_t supported_actions = ret ? 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_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");
+}
+
+
+static void data_device_handle_drop(void *data, struct wl_data_device *data_device) {
+ if (!current_drag_offer) return;
+ 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;
+
+
+static void read_int(uchar *c, int& i) {
+ i = *c;
+ i |= (*(++c))<<8;
+ i |= (*(++c))<<16;
+ i |= (*(++c))<<24;
+}
+
+
+// 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() {
+ int fds[2];
+ if (pipe(fds)) return 1;
+ wl_data_offer_receive(fl_selection_offer, fl_selection_offer_type, fds[1]);
+ close(fds[1]);
+ wl_display_roundtrip(fl_display);
+ if (strcmp(fl_selection_offer_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 == -1) return 1;
+ while (true) {
+ char buf[10000];
+ ssize_t n = read(fds[0], buf, sizeof(buf));
+ if (n <= 0) {
+ close(fds[0]);
+ close(fd);
+ break;
+ }
+ n = write(fd, buf, n);
+ }
+ shared = Fl_Shared_Image::get(tmp_fname);
+ fl_unlink(tmp_fname);
+ 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
+ read_int(buf + 18, w);
+ read_int(buf + 22, h);
+ int R = ((3*w+3)/4) * 4; // the number of bytes per row of BMP image, rounded up to multiple of 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()) return;
+ Window xid = fl_xid(receiver.top_window());
+ if (xid && xid->scale > 1) {
+ Fl_RGB_Image *rgb = (Fl_RGB_Image*)Fl::e_clipboard_data;
+ rgb->scale(rgb->data_w() / xid->scale, rgb->data_h() / xid->scale);
+ }
+ 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) {
+ Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
+ scr_driver->seat->data_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(scr_driver->seat->data_source, &data_source_listener, (void*)1);
+ wl_data_source_offer(scr_driver->seat->data_source, wld_plain_text_clipboard);
+ wl_data_device_set_selection(scr_driver->seat->data_device, scr_driver->seat->data_source, scr_driver->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;
+ 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/X11/Fl_X11_System_Driver.H b/src/drivers/X11/Fl_X11_System_Driver.H
index e39f40706..a5347d74d 100644
--- a/src/drivers/X11/Fl_X11_System_Driver.H
+++ b/src/drivers/X11/Fl_X11_System_Driver.H
@@ -19,47 +19,23 @@
#define FL_X11_SYSTEM_DRIVER_H
#include <config.h>
-#include "../Posix/Fl_Posix_System_Driver.H"
+#include "../Unix/Fl_Unix_System_Driver.H"
-class Fl_X11_System_Driver : public Fl_Posix_System_Driver {
+class Fl_X11_System_Driver : public Fl_Unix_System_Driver {
public:
- Fl_X11_System_Driver() : Fl_Posix_System_Driver() {
+ Fl_X11_System_Driver() : Fl_Unix_System_Driver() {
// X11 system driver does not use a key table
key_table = NULL;
key_table_size = 0;
}
virtual void display_arg(const char *arg);
virtual int XParseGeometry(const char*, int*, int*, unsigned int*, unsigned int*);
- virtual int clocale_printf(FILE *output, const char *format, va_list args);
- virtual int clocale_snprintf(char *output, size_t output_size, const char *format, va_list args);
- virtual int clocale_sscanf(const char *input, const char *format, va_list args);
// these 2 are in Fl_get_key.cxx
virtual int event_key(int k);
virtual int get_key(int k);
- virtual int filename_list(const char *d, dirent ***list,
- int (*sort)(struct dirent **, struct dirent **),
- char *errmsg=NULL, int errmsg_sz=0);
virtual int need_menu_handle_part1_extra() {return 1;}
- virtual int open_uri(const char *uri, char *msg, int msglen);
- virtual int use_tooltip_timeout_condition() {return 1;}
- // this one is in fl_shortcut.cxx
virtual const char *shortcut_add_key_name(unsigned key, char *p, char *buf, const char **);
- virtual int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon);
- virtual void newUUID(char *uuidBuffer);
- virtual char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
- const char *application);
- virtual int preferences_need_protection_check() {return 1;}
- virtual int utf8locale();
- // this one is in Fl_own_colormap.cxx
virtual void own_colormap();
- // this one is in Fl_x.cxx
- virtual const char *filename_name(const char *buf);
- virtual void add_fd(int fd, int when, Fl_FD_Handler cb, void* = 0);
- virtual void add_fd(int fd, Fl_FD_Handler cb, void* = 0);
- virtual void remove_fd(int, int when);
- virtual void remove_fd(int);
- virtual double wait(double time_to_wait);
- virtual int ready();
// 2 additional virtual members
virtual int poll_or_select();
virtual int poll_or_select_with_delay(double time_to_wait);
diff --git a/src/drivers/X11/Fl_X11_System_Driver.cxx b/src/drivers/X11/Fl_X11_System_Driver.cxx
index e62fdf8b8..40a98d32e 100644
--- a/src/drivers/X11/Fl_X11_System_Driver.cxx
+++ b/src/drivers/X11/Fl_X11_System_Driver.cxx
@@ -80,58 +80,6 @@ Fl_System_Driver *Fl_System_Driver::newSystemDriver()
return new Fl_X11_System_Driver();
}
-#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
-static locale_t c_locale = NULL;
-#endif
-
-
-int Fl_X11_System_Driver::clocale_printf(FILE *output, const char *format, va_list args) {
-#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
- if (!c_locale)
- c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
- locale_t previous_locale = uselocale(c_locale);
- int retval = vfprintf(output, format, args);
- uselocale(previous_locale);
-#else
- char *saved_locale = setlocale(LC_NUMERIC, NULL);
- setlocale(LC_NUMERIC, "C");
- int retval = vfprintf(output, format, args);
- setlocale(LC_NUMERIC, saved_locale);
-#endif
- return retval;
-}
-
-int Fl_X11_System_Driver::clocale_snprintf(char *output, size_t output_size, const char *format, va_list args) {
-#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
- if (!c_locale)
- c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
- locale_t previous_locale = uselocale(c_locale);
- int retval = vsnprintf(output, output_size, format, args);
- uselocale(previous_locale);
-#else
- char *saved_locale = setlocale(LC_NUMERIC, NULL);
- setlocale(LC_NUMERIC, "C");
- int retval = vsnprintf(output, output_size, format, args);
- setlocale(LC_NUMERIC, saved_locale);
-#endif
- return retval;
-}
-
-int Fl_X11_System_Driver::clocale_sscanf(const char *input, const char *format, va_list args) {
-#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
- if (!c_locale)
- c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
- locale_t previous_locale = uselocale(c_locale);
- int retval = vsscanf(input, format, args);
- uselocale(previous_locale);
-#else
- char *saved_locale = setlocale(LC_NUMERIC, NULL);
- setlocale(LC_NUMERIC, "C");
- int retval = vsscanf(input, format, args);
- setlocale(LC_NUMERIC, saved_locale);
-#endif
- return retval;
-}
// Find a program in the path...
static char *path_find(const char *program, char *filename, int filesize) {
@@ -166,376 +114,6 @@ static char *path_find(const char *program, char *filename, int filesize) {
}
-int Fl_X11_System_Driver::open_uri(const char *uri, char *msg, int msglen)
-{
- // Run any of several well-known commands to open the URI.
- //
- // We give preference to the Portland group's xdg-utils
- // programs which run the user's preferred web browser, etc.
- // based on the current desktop environment in use. We fall
- // back on older standards and then finally test popular programs
- // until we find one we can use.
- //
- // Note that we specifically do not support the MAILER and
- // BROWSER environment variables because we have no idea whether
- // we need to run the listed commands in a terminal program.
- char command[FL_PATH_MAX], // Command to run...
- *argv[4], // Command-line arguments
- remote[1024]; // Remote-mode command...
- const char * const *commands; // Array of commands to check...
- int i;
- static const char * const browsers[] = {
- "xdg-open", // Portland
- "htmlview", // Freedesktop.org
- "firefox",
- "mozilla",
- "netscape",
- "konqueror", // KDE
- "opera",
- "hotjava", // Solaris
- "mosaic",
- NULL
- };
- static const char * const readers[] = {
- "xdg-email", // Portland
- "thunderbird",
- "mozilla",
- "netscape",
- "evolution", // GNOME
- "kmailservice", // KDE
- NULL
- };
- static const char * const managers[] = {
- "xdg-open", // Portland
- "fm", // IRIX
- "dtaction", // CDE
- "nautilus", // GNOME
- "konqueror", // KDE
- NULL
- };
-
- // Figure out which commands to check for...
- if (!strncmp(uri, "file://", 7)) commands = managers;
- else if (!strncmp(uri, "mailto:", 7) ||
- !strncmp(uri, "news:", 5)) commands = readers;
- else commands = browsers;
-
- // Find the command to run...
- for (i = 0; commands[i]; i ++)
- if (path_find(commands[i], command, sizeof(command))) break;
-
- if (!commands[i]) {
- if (msg) {
- snprintf(msg, msglen, "No helper application found for \"%s\"", uri);
- }
-
- return 0;
- }
-
- // Handle command-specific arguments...
- argv[0] = (char *)commands[i];
-
- if (!strcmp(commands[i], "firefox") ||
- !strcmp(commands[i], "mozilla") ||
- !strcmp(commands[i], "netscape") ||
- !strcmp(commands[i], "thunderbird")) {
- // program -remote openURL(uri)
- snprintf(remote, sizeof(remote), "openURL(%s)", uri);
-
- argv[1] = (char *)"-remote";
- argv[2] = remote;
- argv[3] = 0;
- } else if (!strcmp(commands[i], "dtaction")) {
- // dtaction open uri
- argv[1] = (char *)"open";
- argv[2] = (char *)uri;
- argv[3] = 0;
- } else {
- // program uri
- argv[1] = (char *)uri;
- argv[2] = 0;
- }
-
- if (msg) {
- strlcpy(msg, argv[0], msglen);
-
- for (i = 1; argv[i]; i ++) {
- strlcat(msg, " ", msglen);
- strlcat(msg, argv[i], msglen);
- }
- }
-
- return run_program(command, argv, msg, msglen) != 0;
-}
-
-
-int Fl_X11_System_Driver::file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon)
-{
- int num_files = 0;
-#if defined(_AIX)
- // AIX don't write the mounted filesystems to a file like '/etc/mnttab'.
- // But reading the list of mounted filesystems from the kernel is possible:
- // http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.basetechref/doc/basetrf1/mntctl.htm
- int res = -1, len;
- char *list = NULL, *name;
- struct vmount *vp;
-
- // We always have the root filesystem
- add("/", icon);
- // Get the required buffer size for the vmount structures
- res = mntctl(MCTL_QUERY, sizeof(len), (char *) &len);
- if (!res) {
- // Allocate buffer ...
- list = (char *) malloc((size_t) len);
- if (NULL == list) {
- res = -1;
- } else {
- // ... and read vmount structures from kernel
- res = mntctl(MCTL_QUERY, len, list);
- if (0 >= res) {
- res = -1;
- } else {
- for (int i = 0, vp = (struct vmount *) list; i < res; ++i) {
- name = (char *) vp + vp->vmt_data[VMT_STUB].vmt_off;
- strlcpy(filename, name, lname);
- // Skip the already added root filesystem
- if (strcmp("/", filename) != 0) {
- strlcat(filename, "/", lname);
- browser->add(filename, icon);
- }
- vp = (struct vmount *) ((char *) vp + vp->vmt_length);
- }
- }
- }
- }
- // Note: Executing 'free(NULL)' is allowed and simply do nothing
- free((void *) list);
-#elif defined(__NetBSD__) && defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 300000000)
- // NetBSD don't write the mounted filesystems to a file like '/etc/mnttab'.
- // Since NetBSD 3.0 the system call getvfsstat(2) has replaced getfsstat(2)
- // that is used by getmntinfo(3):
- // http://www.daemon-systems.org/man/getmntinfo.3.html
- int res = -1;
- struct statvfs *list;
-
- // We always have the root filesystem
- browser->add("/", icon);
-# ifdef HAVE_PTHREAD
- // Lock mutex for thread safety
- if (!pthread_mutex_lock(&getvfsstat_mutex)) {
-# endif // HAVE_PTHREAD
- // Get list of statvfs structures
- res = getmntinfo(&list, ST_WAIT);
- if (0 < res) {
- for (int i = 0; i < res; ++i) {
- strlcpy(filename, list[i].f_mntonname, lname);
- // Skip the already added root filesystem
- if (strcmp("/", filename) != 0) {
- strlcat(filename, "/", lname);
- browser->add(filename, icon);
- }
- }
- } else {
- res = -1;
- }
-# ifdef HAVE_PTHREAD
- pthread_mutex_unlock(&getvfsstat_mutex);
- }
-# endif // HAVE_PTHREAD
-#else
- //
- // UNIX code uses /etc/fstab or similar...
- //
- FILE *mtab; // /etc/mtab or /etc/mnttab file
- char line[FL_PATH_MAX]; // Input line
-
- // Every Unix has a root filesystem '/'.
- // This ensures that the user don't get an empty
- // window after requesting filesystem list.
- browser->add("/", icon);
- num_files ++;
-
- //
- // Open the file that contains a list of mounted filesystems...
- //
- // Note: this misses automounted filesystems on FreeBSD if absent from /etc/fstab
- //
-
- mtab = fopen("/etc/mnttab", "r"); // Fairly standard
- if (mtab == NULL)
- mtab = fopen("/etc/mtab", "r"); // More standard
- if (mtab == NULL)
- mtab = fopen("/etc/fstab", "r"); // Otherwise fallback to full list
- if (mtab == NULL)
- mtab = fopen("/etc/vfstab", "r"); // Alternate full list file
-
- if (mtab != NULL)
- {
- while (fgets(line, sizeof(line), mtab) != NULL)
- {
- if (line[0] == '#' || line[0] == '\n')
- continue;
- if (sscanf(line, "%*s%4095s", filename) != 1)
- continue;
- if (strcmp("/", filename) == 0)
- continue; // "/" was added before
-
- // Add a trailing slash (except for the root filesystem)
- strlcat(filename, "/", lname);
-
- // printf("Fl_File_Browser::load() - adding \"%s\" to list...\n", filename);
- browser->add(filename, icon);
- num_files ++;
- }
-
- fclose(mtab);
- }
-#endif // _AIX || ...
- return num_files;
-}
-
-void Fl_X11_System_Driver::newUUID(char *uuidBuffer)
-{
- unsigned char b[16];
-#if HAVE_DLSYM && HAVE_DLFCN_H
- typedef void (*gener_f_type)(uchar*);
- static bool looked_for_uuid_generate = false;
- static gener_f_type uuid_generate_f = NULL;
- if (!looked_for_uuid_generate) {
- looked_for_uuid_generate = true;
- uuid_generate_f = (gener_f_type)Fl_Posix_System_Driver::dlopen_or_dlsym("libuuid", "uuid_generate");
- }
- if (uuid_generate_f) {
- uuid_generate_f(b);
- } else
-#endif
- {
- time_t t = time(0); // first 4 byte
- b[0] = (unsigned char)t;
- b[1] = (unsigned char)(t>>8);
- b[2] = (unsigned char)(t>>16);
- b[3] = (unsigned char)(t>>24);
- int r = rand(); // four more bytes
- b[4] = (unsigned char)r;
- b[5] = (unsigned char)(r>>8);
- b[6] = (unsigned char)(r>>16);
- b[7] = (unsigned char)(r>>24);
- unsigned long a = (unsigned long)&t; // four more bytes
- b[8] = (unsigned char)a;
- b[9] = (unsigned char)(a>>8);
- b[10] = (unsigned char)(a>>16);
- b[11] = (unsigned char)(a>>24);
- // Now we try to find 4 more "random" bytes. We extract the
- // lower 4 bytes from the address of t - it is created on the
- // stack so *might* be in a different place each time...
- // This is now done via a union to make it compile OK on 64-bit systems.
- union { void *pv; unsigned char a[sizeof(void*)]; } v;
- v.pv = (void *)(&t);
- // NOTE: May need to handle big- or little-endian systems here
-# if WORDS_BIGENDIAN
- b[8] = v.a[sizeof(void*) - 1];
- b[9] = v.a[sizeof(void*) - 2];
- b[10] = v.a[sizeof(void*) - 3];
- b[11] = v.a[sizeof(void*) - 4];
-# else // data ordered for a little-endian system
- b[8] = v.a[0];
- b[9] = v.a[1];
- b[10] = v.a[2];
- b[11] = v.a[3];
-# endif
- char name[80]; // last four bytes
- gethostname(name, 79);
- memcpy(b+12, name, 4);
- }
- sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
- b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
- b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
-}
-
-/*
- Note: `prefs` can be NULL!
- */
-char *Fl_X11_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/,
- Fl_Preferences::Root root,
- const char *vendor,
- const char *application)
-{
- static char *filename = 0L;
- if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX);
- const char *home = "";
- int pref_type = root & Fl_Preferences::ROOT_MASK;
- switch (pref_type) {
- case Fl_Preferences::USER:
- home = getenv("HOME");
- // make sure that $HOME is set to an existing directory
- if ((home == NULL) || (home[0] == 0) || (::access(home, F_OK) == -1)) {
- struct passwd *pw = getpwuid(getuid());
- if (pw)
- home = pw->pw_dir;
- }
- if ((home == 0L) || (home[0] == 0) || (::access(home, F_OK) == -1))
- return NULL;
- strlcpy(filename, home, FL_PATH_MAX);
- if (filename[strlen(filename) - 1] != '/')
- strlcat(filename, "/", FL_PATH_MAX);
- strlcat(filename, ".fltk/", FL_PATH_MAX);
- break;
- case Fl_Preferences::SYSTEM:
- strcpy(filename, "/etc/fltk/");
- break;
- default: // MEMORY
- filename[0] = '\0'; // empty string
- break;
- }
-
- // Make sure that the parameters are not NULL
- if ( (vendor==NULL) || (vendor[0]==0) )
- vendor = "unknown";
- if ( (application==NULL) || (application[0]==0) )
- application = "unknown";
-
- snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename),
- "%s/%s.prefs", vendor, application);
-
- // If this is not the USER path (i.e. SYSTEM or MEMORY), we are done
- if ((pref_type) != Fl_Preferences::USER)
- return filename;
-
- // If the legacy file exists, we are also done
- if (::access(filename, F_OK)==0)
- return filename;
-
- // This is USER mode, and there is no legacy file. Create an XDG conforming path.
- // Check $XDG_CONFIG_HOME, and if it isn't set, default to $HOME/.config
- const char *xdg = getenv("XDG_CONFIG_HOME");
- if (xdg==NULL) {
- xdg = "~/.config";
- }
- filename[0] = 0;
- if (strncmp(xdg, "~/", 2)==0) {
- strlcpy(filename, home, FL_PATH_MAX);
- strlcat(filename, "/", FL_PATH_MAX);
- strlcat(filename, xdg+2, FL_PATH_MAX);
- } else if (strncmp(xdg, "$HOME/", 6)==0) {
- strlcpy(filename, home, FL_PATH_MAX);
- strlcat(filename, "/", FL_PATH_MAX);
- strlcat(filename, xdg+6, FL_PATH_MAX);
- } else if (strncmp(xdg, "${HOME}/", 8)==0) {
- strlcpy(filename, home, FL_PATH_MAX);
- strlcat(filename, "/", FL_PATH_MAX);
- strlcat(filename, xdg+8, FL_PATH_MAX);
- } else {
- strlcpy(filename, xdg, FL_PATH_MAX);
- }
- strlcat(filename, "/", FL_PATH_MAX);
- strlcat(filename, vendor, FL_PATH_MAX);
- strlcat(filename, "/", FL_PATH_MAX);
- strlcat(filename, application, FL_PATH_MAX);
- strlcat(filename, ".prefs", FL_PATH_MAX);
-
- return filename;
-}
-
void Fl_X11_System_Driver::display_arg(const char *arg) {
Fl::display(arg);
}
@@ -545,112 +123,6 @@ int Fl_X11_System_Driver::XParseGeometry(const char* string, int* x, int* y,
return ::XParseGeometry(string, x, y, width, height);
}
-//
-// Needs some docs
-// Returns -1 on error, errmsg will contain OS error if non-NULL.
-//
-int Fl_X11_System_Driver::filename_list(const char *d,
- dirent ***list,
- int (*sort)(struct dirent **, struct dirent **),
- char *errmsg, int errmsg_sz) {
- int dirlen;
- char *dirloc;
-
- if (errmsg && errmsg_sz>0) errmsg[0] = '\0';
-
- // Assume that locale encoding is no less dense than UTF-8
- dirlen = strlen(d);
- dirloc = (char *)malloc(dirlen + 1);
- fl_utf8to_mb(d, dirlen, dirloc, dirlen + 1);
-
-#ifndef HAVE_SCANDIR
- // This version is when we define our own scandir. Note it updates errmsg on errors.
- int n = fl_scandir(dirloc, list, 0, sort, errmsg, errmsg_sz);
-#elif defined(HAVE_SCANDIR_POSIX)
- // POSIX (2008) defines the comparison function like this:
- int n = scandir(dirloc, list, 0, (int(*)(const dirent **, const dirent **))sort);
-#elif defined(__osf__)
- // OSF, DU 4.0x
- int n = scandir(dirloc, list, 0, (int(*)(dirent **, dirent **))sort);
-#elif defined(_AIX)
- // AIX is almost standard...
- int n = scandir(dirloc, list, 0, (int(*)(void*, void*))sort);
-#elif defined(__sgi)
- int n = scandir(dirloc, list, 0, sort);
-#else
- // The vast majority of UNIX systems want the sort function to have this
- // prototype, most likely so that it can be passed to qsort without any
- // changes:
- int n = scandir(dirloc, list, 0, (int(*)(const void*,const void*))sort);
-#endif
-
- free(dirloc);
-
- if (n==-1) {
- // Don't write to errmsg if FLTK's fl_scandir() already set it.
- // If OS's scandir() was used (HAVE_SCANDIR), we return its error in errmsg here..
-#ifdef HAVE_SCANDIR
- if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno));
-#endif
- return -1;
- }
-
- // convert every filename to UTF-8, and append a '/' to all
- // filenames that are directories
- int i;
- char *fullname = (char*)malloc(dirlen+FL_PATH_MAX+3); // Add enough extra for two /'s and a nul
- // Use memcpy for speed since we already know the length of the string...
- memcpy(fullname, d, dirlen+1);
-
- char *name = fullname + dirlen;
- if (name!=fullname && name[-1]!='/')
- *name++ = '/';
-
- for (i=0; i<n; i++) {
- int newlen;
- dirent *de = (*list)[i];
- int len = strlen(de->d_name);
- newlen = fl_utf8from_mb(NULL, 0, de->d_name, len);
- dirent *newde = (dirent*)malloc(de->d_name - (char*)de + newlen + 2); // Add space for a / and a nul
-
- // Conversion to UTF-8
- memcpy(newde, de, de->d_name - (char*)de);
- fl_utf8from_mb(newde->d_name, newlen + 1, de->d_name, len);
-
- // Check if dir (checks done on "old" name as we need to interact with
- // the underlying OS)
- if (de->d_name[len-1]!='/' && len<=FL_PATH_MAX) {
- // Use memcpy for speed since we already know the length of the string...
- memcpy(name, de->d_name, len+1);
- if (fl_filename_isdir(fullname)) {
- char *dst = newde->d_name + newlen;
- *dst++ = '/';
- *dst = 0;
- }
- }
-
- free(de);
- (*list)[i] = newde;
- }
- free(fullname);
-
- return n;
-}
-
-int Fl_X11_System_Driver::utf8locale() {
- static int ret = 2;
- if (ret == 2) {
- char* s;
- ret = 1; /* assume UTF-8 if no locale */
- if (((s = getenv("LC_CTYPE")) && *s) ||
- ((s = getenv("LC_ALL")) && *s) ||
- ((s = getenv("LANG")) && *s)) {
- ret = (strstr(s,"utf") || strstr(s,"UTF"));
- }
- }
- return ret;
-}
-
#if !defined(FL_DOXYGEN)
@@ -700,30 +172,4 @@ void Fl_X11_System_Driver::own_colormap() {
#endif // USE_COLORMAP
}
-int Fl_X11_System_Driver::ready() {
- Fl_Timeout::elapse_timeouts();
- if (Fl_Timeout::time_to_wait(1.0) <= 0.0) return 1;
- return this->poll_or_select();
-}
-
-double Fl_X11_System_Driver::wait(double time_to_wait)
-{
- if (time_to_wait <= 0.0) {
- // do flush second so that the results of events are visible:
- int ret = this->poll_or_select_with_delay(0.0);
- Fl::flush();
- return ret;
- } else {
- // do flush first so that user sees the display:
- Fl::flush();
- if (Fl::idle) // 'idle' may have been set within flush()
- time_to_wait = 0.0;
- else {
- Fl_Timeout::elapse_timeouts();
- time_to_wait = Fl_Timeout::time_to_wait(time_to_wait);
- }
- return this->poll_or_select_with_delay(time_to_wait);
- }
-}
-
#endif // !defined(FL_DOXYGEN)