Add the Wayland platform to FLTK 1.4

This commit is contained in:
ManoloFLTK 2022-03-04 15:40:29 +01:00
parent a773fdc44b
commit 3718effc43
61 changed files with 18951 additions and 897 deletions

View File

@ -1,4 +1,4 @@
Changes in FLTK 1.4.0 Released: ??? ?? 2021
Changes in FLTK 1.4.0 Released: ??? ?? 2022
General Information about this Release
@ -17,6 +17,8 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2021
New Features and Extensions
- FLTK 1.4 introduces a new platform, Wayland, available for recent Unix
distributions. More information in README.Wayland.txt
- Windows platform: added support for using a manifest to set the
application's level of DPI awareness (issue #309).
- X11 platform: class Fl_Native_File_Chooser will run the KDE file dialog
@ -121,6 +123,8 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2021
New Configuration Options (ABI Version)
- Configure option --enable-wayland allows to build the FLTK library for
the new Wayland platform. The corresponding CMake option is OPTION_USE_WAYLAND.
- The new configure option --disable-gdiplus removes the possibility to draw
antialiased lines and curves on the Windows platform. The corresponding CMake
option is OPTION_USE_GDIPLUS.

View File

@ -50,6 +50,24 @@ set (FL_ABI_VERSION ${OPTION_ABI_VERSION})
if (UNIX)
option (OPTION_CREATE_LINKS "create backwards compatibility links" OFF)
list (APPEND FLTK_LDLIBS -lm)
option (OPTION_USE_WAYLAND "use Wayland" OFF)
if (OPTION_USE_WAYLAND)
set (FLTK_USE_WAYLAND 1)
option (OPTION_USE_SYSTEM_LIBDECOR "use libdecor from the system" OFF)
unset (OPTION_USE_XRENDER CACHE)
unset (OPTION_USE_XINERAMA CACHE)
unset (OPTION_USE_XFT CACHE)
unset (OPTION_USE_XCURSOR CACHE)
unset (OPTION_USE_XFIXES CACHE)
unset (OPTION_USE_PANGO CACHE)
set (OPTION_USE_PANGO TRUE CACHE BOOL "use lib Pango")
if (OPTION_USE_SYSTEM_LIBDECOR)
pkg_check_modules(SYSTEM_LIBDECOR libdecor-0)
if (NOT SYSTEM_LIBDECOR_FOUND)
set (OPTION_USE_SYSTEM_LIBDECOR OFF)
endif (NOT SYSTEM_LIBDECOR_FOUND)
endif (OPTION_USE_SYSTEM_LIBDECOR)
endif (OPTION_USE_WAYLAND)
endif (UNIX)
if (WIN32)
@ -72,7 +90,7 @@ endif (APPLE)
# find X11 libraries and headers
set (PATH_TO_XLIBS)
if ((NOT APPLE OR OPTION_APPLE_X11) AND NOT WIN32)
if ((NOT APPLE OR OPTION_APPLE_X11) AND NOT WIN32 AND NOT OPTION_USE_WAYLAND)
include (FindX11)
if (X11_FOUND)
set (FLTK_USE_X11 1)
@ -82,7 +100,7 @@ if ((NOT APPLE OR OPTION_APPLE_X11) AND NOT WIN32)
endif (X11_Xext_FOUND)
get_filename_component (PATH_TO_XLIBS ${X11_X11_LIB} PATH)
endif (X11_FOUND)
endif ((NOT APPLE OR OPTION_APPLE_X11) AND NOT WIN32)
endif ((NOT APPLE OR OPTION_APPLE_X11) AND NOT WIN32 AND NOT OPTION_USE_WAYLAND)
if (OPTION_APPLE_X11)
if (NOT(${CMAKE_SYSTEM_VERSION} VERSION_LESS 17.0.0)) # a.k.a. macOS version 10.13
@ -252,6 +270,8 @@ if (OPENGL_FOUND)
set (GLLIBS "-lglu32 -lopengl32")
elseif (APPLE AND NOT OPTION_APPLE_X11)
set (GLLIBS "-framework OpenGL")
elseif (OPTION_USE_WAYLAND)
set (GLLIBS "-lwayland-egl -lEGL -lGLU -lGL")
else ()
set (GLLIBS "-lGLU -lGL")
endif (WIN32)
@ -481,7 +501,7 @@ if (X11_Xft_FOUND)
endif (X11_Xft_FOUND)
# test option compatibility: Pango requires Xft
if (OPTION_USE_PANGO)
if (OPTION_USE_PANGO AND NOT OPTION_USE_WAYLAND)
if (NOT X11_Xft_FOUND)
message (STATUS "Pango requires Xft but Xft library or headers could not be found.")
message (STATUS "Please install Xft development files and try again or disable OPTION_USE_PANGO.")
@ -493,10 +513,10 @@ if (OPTION_USE_PANGO)
message (FATAL_ERROR "*** Aborting ***")
endif (NOT OPTION_USE_XFT)
endif (NOT X11_Xft_FOUND)
endif (OPTION_USE_PANGO)
endif (OPTION_USE_PANGO AND NOT OPTION_USE_WAYLAND)
#######################################################################
if (X11_Xft_FOUND AND OPTION_USE_PANGO)
if ((X11_Xft_FOUND OR OPTION_USE_WAYLAND) AND OPTION_USE_PANGO)
pkg_check_modules(PANGOXFT pangoxft)
pkg_check_modules(PANGOCAIRO pangocairo)
pkg_check_modules(CAIRO cairo)
@ -553,7 +573,15 @@ if (X11_Xft_FOUND AND OPTION_USE_PANGO)
list (APPEND FLTK_LDLIBS -lpango-1.0 -lpangoxft-1.0 -lgobject-2.0)
endif (HAVE_LIB_PANGO AND HAVE_LIB_PANGOXFT AND HAVE_LIB_GOBJECT)
endif (PANGOXFT_FOUND AND PANGOCAIRO_FOUND AND CAIRO_FOUND)
endif (X11_Xft_FOUND AND OPTION_USE_PANGO)
endif ((X11_Xft_FOUND OR OPTION_USE_WAYLAND) AND OPTION_USE_PANGO)
if (OPTION_USE_WAYLAND AND NOT OPTION_USE_SYSTEM_LIBDECOR)
pkg_check_modules(GTK gtk+-3.0)
#set (GTK_FOUND 0) #use this to get cairo titlebars rather than GTK
if (GTK_FOUND)
include_directories (${GTK_INCLUDE_DIRS})
endif (GTK_FOUND)
endif (OPTION_USE_WAYLAND AND NOT OPTION_USE_SYSTEM_LIBDECOR)
if (OPTION_USE_XFT)
set (USE_XFT X11_Xft_FOUND)

View File

@ -43,6 +43,11 @@ if (WIN32)
list (APPEND FLTK_LDLIBS -lole32 -luuid -lcomctl32 -lws2_32)
elseif (APPLE AND NOT OPTION_APPLE_X11)
list (APPEND FLTK_LDLIBS "-framework Cocoa")
elseif (OPTION_USE_WAYLAND)
list (APPEND FLTK_LDLIBS "-lwayland-cursor -lwayland-client -lxkbcommon -ldbus-1")
if (OPTION_USE_SYSTEM_LIBDECOR)
list (APPEND FLTK_LDLIBS "-ldecor-0")
endif (OPTION_USE_SYSTEM_LIBDECOR)
else ()
list (APPEND FLTK_LDLIBS -lm)
endif (WIN32)

View File

@ -37,6 +37,8 @@ class Fl_Window;
# include "win32.H"
# elif defined(__APPLE__)
# include "mac.H"
# elif defined(FLTK_USE_WAYLAND)
# include "wayland.H"
# elif defined(FLTK_USE_X11)
# include "x11.H"
# endif // _WIN32

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2018 by Bill Spitzak and others.
* Copyright 2016-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
@ -116,6 +116,17 @@ typedef struct HGLRC__ *GLContext;
struct dirent {char d_name[1];};
#endif
#elif defined(FLTK_USE_WAYLAND)
typedef struct fl_wld_buffer *Fl_Offscreen; /**< an offscreen drawing buffer */
typedef struct _cairo_pattern* Fl_Bitmask;
typedef struct flWaylandRegion* Fl_Region;
typedef int FL_SOCKET; /**< socket or file descriptor */
typedef void *EGLContext;
typedef EGLContext GLContext;
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#elif defined(FLTK_USE_X11)
typedef unsigned long Fl_Offscreen;

31
FL/wayland.H Executable file
View File

@ -0,0 +1,31 @@
//
// Wayland platform header file 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_PLATFORM_H)
# error "Never use <FL/wayland.H> directly; include <FL/platform.H> instead."
#endif // !FL_PLATFORM_H
typedef struct wld_window *Window;
extern struct wl_display *fl_display;
struct flWaylandRegion {
int count;
struct _cairo_rectangle *rects;
}; // a region is the union of a series of rectangles
#include <stdint.h>
extern FL_EXPORT uint32_t fl_event_time;

141
README.Wayland.txt Normal file
View File

@ -0,0 +1,141 @@
README.Wayland.txt - Wayland platform support for FLTK
------------------------------------------------------
CONTENTS
========
1 INTRODUCTION
2 WAYLAND SUPPORT FOR FLTK
2.1 Configuration
2.2 Known limitations
3 PLATFORM SPECIFIC NOTES
3.1 Debian and Derivatives (like Ubuntu)
1 INTRODUCTION
==============
Version 1.4 of the FLTK library introduces support of the public FLTK API on
the Wayland platform. It requires a Wayland-equipped OS which means Linux.
Pre-existing platform-independent source code for FLTK 1.3.x should build and
run unchanged with FLTK 1.4 and the Wayland platform.
The code has been tested on Debian and Ubuntu with 3 distinct Wayland compositors:
mutter (Debian's compositor), weston, and KDE.
CJK text-input methods, as well as dead and compose keys are supported.
2 WAYLAND SUPPORT FOR FLTK
==========================
It is possible to have your FLTK application do all its windowing and drawing
through the Wayland protocol on Linux systems. All graphics is done via Cairo or EGL.
All text-drawing is done via Pango.
2.1 Configuration
---------------
* Configure-based build can be performed as follows:
Once after "git clone", create the configure file :
autoconf -f
Prepare build with :
./configure --enable-wayland [--enable-shared]
Build with :
make
* CMake-based build can be performed as follows:
cmake -S <path-to-source> -B <path-to-build> -DCMAKE_BUILD_TYPE=Release -DOPTION_USE_WAYLAND=1
cd <path-to-build>; make
The FLTK wayland platform uses a library called libdecor which handles window decorations
(i.e., titlebars, shade). Libdecor is bundled in the FLTK source code and FLTK uses by default
this form of libdecor. Optionally, OPTION_USE_SYSTEM_LIBDECOR can be turned on to have FLTK
use the system's version of libdecor which is available on recent Linux distributions (e.g.,
Debian bookworm or more recent in packages libdecor-0-0 and libdecor-0-plugin-1-cairo).
2.2 Known limitations
-------------------------------
* A deliberate design trait of Wayland makes application windows ignorant of their exact
placement on screen. It's possible, though, to position a popup window relatively to another
window. This allows FLTK to properly position menu and tooltip windows. But Fl_Window::position()
has no effect on other top-level windows.
* With Wayland, there is no way to know if a window is currently minimized, nor is there any
way to programmatically unset minimization of a window. Consequently, Fl_Window::show() of
a minimized window has no effect.
* With GTK-style window titlebars, the minimum width of a window is currently set at 134 pixels.
* The library should support multi-display configurations in principle, but has not been
tested in that situation.
* Text input methods have been tested without any understanding of the writing systems,
so feedback on this subject would be helpful.
* While platform-independent source code prepared for FLTK 1.3 is expected to be compatible
with FLTK 1.4 and the Wayland platform, X11-specific code will not compile. In particular,
the common FLTK 1.3 construct :
#ifdef __APPLE__
*** macOS-specific code ***
#elif defined(_WIN32)
*** Windows-specific code ***
#else
*** X11-specific code ***
#endif
will choke at compile time because it exposes X11-specific code to the non-X11, Wayland
environment. This should be written instead :
#include <FL/fl_config.h>
#ifdef __APPLE__
*** macOS-specific code ***
#elif defined(_WIN32)
*** Windows-specific code ***
#elif defined(FLTK_USE_X11)
*** X11-specific code ***
#endif
Moreover, the new FLTK_USE_WAYLAND preprocessor variable is available to bracket
Wayland-specific source code.
3 PLATFORM SPECIFIC NOTES
=========================
The following are notes about building FLTK for the Wayland platform
on the various supported Linux distributions.
3.1 Debian and Derivatives (like Ubuntu)
----------------------------------------
Under Debian, the Wayland platform requires version 11 (a.k.a. Bullseye) or more recent.
Under Ubuntu, the Wayland platform is known to work with version 20.04 (focal fossa) or more recent.
These packages are necessary to build the FLTK library, in addition to those present
in a basic Debian/Ubuntu distribution :
- g++
- gdb
- make
- git
- autoconf
- libglu1-mesa-dev
- libpango1.0-dev
- libwayland-dev
- wayland-protocols
- libdbus-1-dev
- libxkbcommon-dev
- libgtk-3-dev <== with this, windows get a GTK-style titlebar
- libglew-dev <== necessary to use OpenGL version 3 or above
- cmake <== if you plan to build with CMake
- cmake-qt-gui <== if you plan to use the GUI of CMake
These further packages are necessary to run FLTK apps under the Gnome-Wayland desktop:
- gnome
- gnome-session-wayland
These packages allow to run FLTK apps under the KDE/Plasma-Wayland desktop:
- kde-plasma-desktop
- plasma-workspace-wayland

View File

@ -1,7 +1,7 @@
//
// Main header file for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2021 by Bill Spitzak and others.
// 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
@ -30,6 +30,9 @@
# include <cairo-win32.h>
#elif defined(__APPLE_QUARTZ__) // macOS
# include <cairo-quartz.h>
#elif defined(FLTK_USE_WAYLAND)
# include "../src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H"
# include "../src/drivers/Wayland/Fl_Wayland_Window_Driver.H"
#else
# error Cairo is not supported on this platform.
#endif
@ -69,7 +72,13 @@ void Fl_Cairo_State::autolink(bool b) {
*/
cairo_t * Fl::cairo_make_current(Fl_Window* wi) {
if (!wi) return NULL; // Precondition
cairo_t * cairo_ctxt;
#if defined(FLTK_USE_WAYLAND)
Window xid = fl_xid(wi);
if (!xid->buffer) return NULL; // this may happen with GL windows
cairo_ctxt = xid->buffer->cairo_;
cairo_state_.cc(cairo_ctxt, false);
#else // FLTK_USE_WAYLAND
if (fl_gc==0) { // means remove current cc
Fl::cairo_cc(0); // destroy any previous cc
cairo_state_.window(0);
@ -82,7 +91,6 @@ cairo_t * Fl::cairo_make_current(Fl_Window* wi) {
cairo_state_.window(wi);
cairo_t * cairo_ctxt;
#ifndef __APPLE__
float scale = Fl::screen_scale(wi->screen_num()); // get the screen scaling factor
#endif
@ -95,9 +103,11 @@ cairo_t * Fl::cairo_make_current(Fl_Window* wi) {
#ifndef __APPLE__
cairo_scale(cairo_ctxt, scale, scale);
#endif
#endif // FLTK_USE_WAYLAND
return cairo_ctxt;
}
#if !defined(FLTK_USE_WAYLAND)
/*
Creates transparently a cairo_surface_t object.
gc is an HDC context in Windows, a CGContext* in Quartz, and
@ -176,6 +186,9 @@ cairo_t * Fl::cairo_make_current(void *gc, int W, int H) {
cairo_surface_destroy(s);
return c;
}
#endif // !FLTK_USE_WAYLAND
#else
// just don't leave the libfltk_cairo lib empty to avoid warnings
#include <FL/Fl_Export.H>

View File

@ -3,7 +3,7 @@ dnl the "configure" script is made from this by running GNU "autoconf"
dnl
dnl Configuration script for the Fast Light Tool Kit (FLTK).
dnl
dnl Copyright 1998-2021 by Bill Spitzak and others.
dnl Copyright 1998-2022 by Bill Spitzak and others.
dnl
dnl This library is free software. Distribution and use rights are outlined in
dnl the file "COPYING" which should have been included with this file. If this
@ -110,6 +110,8 @@ AC_ARG_ENABLE([localzlib], AS_HELP_STRING([--enable-localzlib], [use local ZLIB
AC_ARG_ENABLE([pango], AS_HELP_STRING([--enable-pango], [turn on Pango support]))
AC_ARG_ENABLE([wayland], AS_HELP_STRING([--enable-wayland], [turn on Wayland support]))
AC_ARG_ENABLE([print], AS_HELP_STRING([--disable-print], [turn off print support (X11)]))
AS_IF([test x$enable_print = xno], [
AC_DEFINE([FL_NO_PRINT_SUPPORT], [Disable X11 print support?])
@ -897,6 +899,7 @@ dnl Define OS-specific stuff...
HLINKS=
OSX_ONLY=:
THREADS=
LIBDECORDIR=""
AC_ARG_WITH([links], AS_HELP_STRING([--with-links], [make header links for common misspellings (default=no)]))
@ -995,6 +998,64 @@ AS_CASE([$host_os_gui], [cygwin* | mingw*], [
THREADS="threads$EXEEXT"
])
AS_IF([test x$enable_wayland = xyes], [
dnl Prepare for Wayland...
AS_IF([test x$PKGCONFIG = x], [
dnl pkg-config is not available, issue warning and abort...
AC_MSG_WARN([--enable-wayland: please install pkg-config.])
AC_MSG_ERROR([Aborting.])
])
BUILD="WAYLAND"
AC_DEFINE([FLTK_USE_WAYLAND])
CFLAGS="$CFLAGS -DUSE_SYSTEM_LIBDECOR=0"
CXXFLAGS="$CXXFLAGS -DUSE_SYSTEM_LIBDECOR=0"
graphics="Wayland"
LIBS="$LIBS $($PKGCONFIG --libs wayland-cursor) $($PKGCONFIG --libs wayland-client) $($PKGCONFIG --libs xkbcommon)"
LIBS="$LIBS $($PKGCONFIG --libs dbus-1) -ldl"
CXXFLAGS="$CXXFLAGS -I../libdecor/src"
DSOFLAGS="$LIBS $DSOFLAGS"
enable_pango=yes
LIBDECORDIR="libdecor/build"
LDFLAGS="$LDFLAGS -rdynamic -no-pie"
AC_SEARCH_LIBS([dlopen], [dl])
AC_CHECK_HEADER([GL/gl.h], [AC_DEFINE([HAVE_GL])])
AC_CHECK_HEADER([GL/glu.h], [
AC_DEFINE([HAVE_GL_GLU_H])
GLLIBS="$($PKGCONFIG --libs wayland-egl) $($PKGCONFIG --libs egl) $($PKGCONFIG --libs glu) $GLLIBS"
])
dnl Check for GTK-3 ...
gtk_found=no
CFLAGS="$($PKGCONFIG --cflags gtk+-3.0) $CFLAGS"
AC_CHECK_HEADERS([gtk/gtk.h], [
CFLAGS="$CFLAGS -DHAVE_GTK"
LIBS="$LIBS $($PKGCONFIG --libs gtk+-3.0)"
gtk_found=yes
])
dnl Check for the Pango library ...
pango_found=no
CFLAGS="$($PKGCONFIG --cflags pangocairo) $CFLAGS"
CXXFLAGS="$($PKGCONFIG --cflags pangocairo) $CXXFLAGS"
LIBS="$LIBS $($PKGCONFIG --libs pangocairo)"
AC_CHECK_HEADERS([pango/pangocairo.h], [
AC_DEFINE([USE_PANGO])
AC_DEFINE([USE_XFT])
pango_found=yes
])
dnl Early abort if Pango could not be found
AS_IF([test x$pango_found != xyes], [
AC_MSG_NOTICE([--enable-wayland: Pango libs and/or headers could not be found.])
AC_MSG_ERROR([Aborting.])
])
], [
dnl Check for X11...
AC_PATH_XTRA
@ -1016,7 +1077,7 @@ AS_CASE([$host_os_gui], [cygwin* | mingw*], [
AS_IF([test "x$x_includes" != x], [
ac_cpp="$ac_cpp -I$x_includes"
])
])
dnl Check for OpenGL unless disabled...
GLLIBS=
@ -1217,6 +1278,8 @@ AC_SUBST([HLINKS])
AC_SUBST([OSX_ONLY])
AC_SUBST([THREADS])
AC_SUBST([LIBDECORDIR])
AC_SUBST([INSTALL_DESKTOP])
AC_SUBST([UNINSTALL_DESKTOP])
@ -1344,17 +1407,21 @@ AS_IF([test -n "$GCC"], [
[AC_MSG_RESULT(no)])
CFLAGS="$OLDCFLAGS"
dnl Make sure that shared libraries don't have undefined references
# See if ld supports -no-undefined...
AC_MSG_CHECKING([if ld supports -no-undefined])
OLDLDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,-no-undefined"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[]], [[]])],
[DSOFLAGS="$DSOFLAGS -Wl,-no-undefined"
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)])
LDFLAGS="$OLDLDFLAGS"
AS_IF([test x$enable_wayland = xyes],[
DSOFLAGS="$DSOFLAGS -Wl,--allow-shlib-undefined"
] , [
dnl Make sure that shared libraries don't have undefined references
# See if ld supports -no-undefined...
AC_MSG_CHECKING([if ld supports -no-undefined])
OLDLDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,-no-undefined"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[]], [[]])],
[DSOFLAGS="$DSOFLAGS -Wl,-no-undefined"
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)])
LDFLAGS="$OLDLDFLAGS"
])
# See if ld supports -Bsymbolic-functions...
AC_MSG_CHECKING([if ld supports -Bsymbolic-functions])
@ -1529,7 +1596,9 @@ AS_CASE([$host_os_gui], [cygwin* | mingw*], [
], [darwin*], [
graphics="Quartz"
], [*], [
graphics="X11"
AS_IF([test x$enable_wayland != xyes], [
graphics="X11"
])
AS_IF([test x$xft_found = xyes], [
graphics="$graphics + Xft"
])

View File

@ -1,7 +1,7 @@
//
// Tiny OpenGL v3 + glut demo program for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2021 by Bill Spitzak and others.
// 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
@ -14,6 +14,7 @@
// https://www.fltk.org/bugs.php
//
#include <FL/Fl.H> // includes <FL/fl_config.h>
#if defined(__APPLE__)
# define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED 1
# include <OpenGL/gl3.h> // defines OpenGL 3.0+ functions
@ -198,7 +199,10 @@ int main (int argc, char* argv[])
glutCreateWindow("Triangle Test");
#ifndef __APPLE__
GLenum err = glewInit(); // defines pters to functions of OpenGL V 1.2 and above
if (err) Fl::error("glewInit() failed returning %u", err);
#ifdef FLTK_USE_WAYLAND
if (err == GLEW_ERROR_NO_GLX_DISPLAY) err = GLEW_OK;
#endif
if (err != GLEW_OK) Fl::error("glewInit() failed returning %u", err);
fprintf(stderr, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
#endif
int gl_version_major;

View File

@ -1,7 +1,7 @@
//
// Tiny OpenGL v3 demo program for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2018 by Bill Spitzak and others.
// 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
@ -146,6 +146,11 @@ public:
make_current();
#ifndef __APPLE__
GLenum err = glewInit(); // defines pters to functions of OpenGL V 1.2 and above
# ifdef FLTK_USE_WAYLAND
// glewInit returns GLEW_ERROR_NO_GLX_DISPLAY with Wayland
// see https://github.com/nigels-com/glew/issues/273
if (err == GLEW_ERROR_NO_GLX_DISPLAY) err = GLEW_OK;
# endif
if (err) Fl::warning("glewInit() failed returning %u", err);
else add_output("Using GLEW %s\n", glewGetString(GLEW_VERSION));
#endif

View File

@ -55,4 +55,14 @@
#cmakedefine FLTK_USE_X11 1
/*
* FLTK_USE_WAYLAND
*
* Do we use Wayland for the current platform?
*
*/
#cmakedefine FLTK_USE_WAYLAND 1
#endif /* _FL_fl_config_h_ */

View File

@ -54,4 +54,14 @@
#undef FLTK_USE_X11
/*
* FLTK_USE_WAYLAND
*
* Do we use Wayland for the current platform?
*
*/
#undef FLTK_USE_WAYLAND
#endif /* _FL_fl_config_h_ */

19
libdecor/LICENSE Normal file
View File

@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

86
libdecor/README.md Normal file
View File

@ -0,0 +1,86 @@
# libdecor - A client-side decorations library for Wayland client
libdecor is a library that can help Wayland clients draw window
decorations for them. It aims to provide multiple backends that implements the
decoration drawing.
## Dependencies
Required:
- `meson` >= 0.47
- `ninja`
- `wayland-client` >= 1.18
- `wayland-protocols` >= 1.15
- `wayland-cursor`
- `cairo`
- `pangocairo`
Recommended:
- `dbus-1` (to query current cursor theme)
Optional
- `egl` (to build EGL example)
- `opengl`
- `xkbcommon` (to build cairo demo)
Install via apt:
`sudo apt install meson libwayland-dev wayland-protocols libpango1.0-dev libdbus-1-dev libegl-dev libopengl-dev libxkbcommon-dev`
Install via dnf:
`sudo dnf install meson wayland-devel wayland-protocols-devel pango-devel dbus-devel mesa-libEGL-devel libglvnd-devel libxkbcommon-devel`
Newer meson versions can be installed via pip: `pip3 install -U meson`.
## Build & Install
### Quick Start
To build and run the example program:
1. `meson build -Dinstall_demo=true && meson compile -C build`
2. `meson devenv -C build libdecor-demo`
### Release Builds
The library and default plugins can be built and installed via:
1. `meson build --buildtype release`
2. `meson install -C build`
where `build` is the build directory that will be created during this process.
This will install by default to `/usr/local/`. To change this set the `prefix` during built, e.g. `meson build --buildtype release -Dprefix=$HOME/.local/`.
Plugins will be installed into the same directory and from thereon will be selected automatically depending on their precedence. This behaviour can be overridden at runtime by setting the environment variable `LIBDECOR_PLUGIN_DIR` and pointing it to a directory with a valid plugin.
### Debug and Development Builds
During development and when debugging, it is recommended to enable the AddressSanitizer and increase the warning level:
1. `meson build -Dinstall_demo=true -Db_sanitize=address -Dwarning_level=3`
2. `meson compile -C build`
You may have to install `libasan6` (apt) or `libasan` (dnf). Otherwise linking will fail.
By default `libdecor` will look for plugins in the target directory of the installation. Therefore, when running the demos directly from the `build` directory, no plugins will be found and the fallback plugin without any decorations will be used.
On Meson 0.58.0 and above, this can be corrected using `devenv`, i.e., to run the demo:
`meson devenv -C build libdecor-demo`
On older Meson versions, the search path for plugins can be overridden by the environment variable `LIBDECOR_PLUGIN_DIR`. To use the `cairo` plugin, point to the plugin directory:
`export LIBDECOR_PLUGIN_DIR=build/src/plugins/cairo/`
and run the demo:
`./build/demo/libdecor-demo`.
### Code of Conduct
libdecor follows the Contributor Covenant, found at:
https://www.freedesktop.org/wiki/CodeOfConduct
Please conduct yourself in a respectful and civilised manner when interacting
with community members on mailing lists, IRC, or bug trackers. The community
represents the project as a whole, and abusive or bullying behaviour is not
tolerated by the project.

80
libdecor/build/Makefile Normal file
View File

@ -0,0 +1,80 @@
#
# Library Makefile for the Fast Light Tool Kit (FLTK).
#
# Copyright 2022 by Bill Spitzak and others.
#
# This library is free software. Distribution and use rights are outlined in
# the file "COPYING" which should have been included with this file. If this
# file is missing or damaged, see the license at:
#
# https://www.fltk.org/COPYING.php
#
# Please see the following page on how to report bugs and issues:
#
# https://www.fltk.org/bugs.php
#
include ../../makeinclude
CFLAGS_DECOR = -I. -I../.. -I../../src -I../src -fPIC -D_GNU_SOURCE -DUSE_SYSTEM_LIBDECOR=0
OBJECTS = fl_libdecor.o libdecor-cairo-blur.o fl_libdecor-plugins.o \
../../src/xdg-decoration-protocol.o ../../src/xdg-shell-protocol.o \
../../src/text-input-protocol.o cursor-settings.o os-compatibility.o
PROTOCOLS = /usr/share/wayland-protocols
all : demo egl
depend:
: echo "libdecor/build: make depend..."
fl_libdecor.o : fl_libdecor.c ../src/libdecor.c ../../src/xdg-shell-protocol.c ../../src/xdg-decoration-protocol.c ../../src/text-input-protocol.c
$(CC) $(CFLAGS) $(CFLAGS_DECOR) -c fl_libdecor.c -DLIBDECOR_PLUGIN_API_VERSION=1 -DLIBDECOR_PLUGIN_DIR=\"/usr/local/lib\"
fl_libdecor-plugins.o : fl_libdecor-plugins.c ../src/plugins/cairo/libdecor-cairo.c
$(CC) $(CFLAGS) $(CFLAGS_DECOR) -c fl_libdecor-plugins.c -DLIBDECOR_PLUGIN_API_VERSION=1 -DLIBDECOR_PLUGIN_DIR=\"/usr/local/lib\"
libdecor-cairo-blur.o : ../src/plugins/cairo/libdecor-cairo-blur.c
$(CC) $(CFLAGS_DECOR) -c ../src/plugins/cairo/libdecor-cairo-blur.c
os-compatibility.o : ../src/os-compatibility.c
$(CC) $(CFLAGS_DECOR) -c ../src/os-compatibility.c
cursor-settings.o : ../src/cursor-settings.c
$(CC) $(CFLAGS_DECOR) -c ../src/cursor-settings.c -DHAS_DBUS `pkg-config --cflags dbus-1`
../../src/xdg-shell-protocol.c :
wayland-scanner private-code $(PROTOCOLS)/stable/xdg-shell/xdg-shell.xml \
../../src/xdg-shell-protocol.c
wayland-scanner client-header $(PROTOCOLS)/stable/xdg-shell/xdg-shell.xml \
../../src/xdg-shell-client-protocol.h
../../src/xdg-decoration-protocol.c :
wayland-scanner private-code \
$(PROTOCOLS)/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml \
../../src/xdg-decoration-protocol.c
wayland-scanner client-header \
$(PROTOCOLS)/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml \
../../src/xdg-decoration-client-protocol.h
../../src/text-input-protocol.c :
wayland-scanner private-code \
$(PROTOCOLS)/unstable/text-input/text-input-unstable-v3.xml \
../../src/text-input-protocol.c
wayland-scanner client-header \
$(PROTOCOLS)/unstable/text-input/text-input-unstable-v3.xml \
../../src/text-input-client-protocol.h
demo : ../demo/demo.c $(OBJECTS)
$(CC) -o demo ../demo/demo.c -D_GNU_SOURCE -I../.. -I../src -I. -I../../src $(OBJECTS) $(LDLIBS) -lm -rdynamic -no-pie -Wl,--defsym=fl_libdecor_using_weston=0
egl : ../demo/egl.c $(OBJECTS)
$(CC) -o egl ../demo/egl.c -D_GNU_SOURCE -I../.. -I../src -I. -I../../src $(OBJECTS) $(GLDLIBS) -lm -rdynamic -no-pie -Wl,--defsym=fl_libdecor_using_weston=0
install:
echo "Nothing to install"
uninstall:
clean:
$(RM) *.o ../../src/xdg-*.c ../../src/xdg-*.h ../../src/xdg-*.o ../../src/text-input-* demo egl

View File

@ -0,0 +1,348 @@
//
// Interface with the libdecor library for the Fast Light Tool Kit (FLTK).
//
// Copyright 2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
/* Support of interactions between FLTK and libdecor plugins, either dynamically
loaded by dlopen() or built-in FLTK.
Under USE_SYSTEM_LIBDECOR, the plugin can only be dynamically loaded.
Under ! USE_SYSTEM_LIBDECOR, it can be dynamically loaded from a directory
given in environment variable LIBDECOR_PLUGIN_DIR, or the built-in one is used.
*/
#include <dlfcn.h>
#include <string.h>
#include "../src/libdecor.h"
#include "xdg-decoration-client-protocol.h"
#include <pango/pangocairo.h>
#if USE_SYSTEM_LIBDECOR
#include "../src/libdecor-plugin.h"
enum component {NONE};
enum decoration_type {DECORATION_TYPE_NONE};
struct buffer { // identical in libdecor-cairo.c and libdecor-gtk.c
struct wl_buffer *wl_buffer;
bool in_use;
bool is_detached;
void *data;
size_t data_size;
int width;
int height;
int scale;
int buffer_width;
int buffer_height;
};
#else // !USE_SYSTEM_LIBDECOR
const struct libdecor_plugin_description *fl_libdecor_plugin_description = NULL;
# ifdef HAVE_GTK
# include <gtk/gtk.h>
# include "../src/plugins/gtk/libdecor-gtk.c"
# else
# include "../src/plugins/cairo/libdecor-cairo.c"
# undef libdecor_frame_set_min_content_size
# endif // HAVE_GTK
#endif // USE_SYSTEM_LIBDECOR
#if USE_SYSTEM_LIBDECOR || HAVE_GTK
/* these definitions derive from libdecor/src/plugins/cairo/libdecor-cairo.c */
struct libdecor_plugin_cairo {
struct libdecor_plugin plugin;
struct wl_callback *globals_callback;
struct wl_callback *globals_callback_shm;
struct libdecor *context;
struct wl_registry *wl_registry;
struct wl_subcompositor *wl_subcompositor;
struct wl_compositor *wl_compositor;
struct wl_shm *wl_shm;
struct wl_callback *shm_callback;
bool has_argb;
struct wl_list visible_frame_list;
struct wl_list seat_list;
struct wl_list output_list;
char *cursor_theme_name;
int cursor_size;
PangoFontDescription *font;
};
enum composite_mode {
COMPOSITE_SERVER,
COMPOSITE_CLIENT,
};
struct border_component_cairo {
enum component type;
bool is_hidden;
bool opaque;
enum composite_mode composite_mode;
struct {
struct wl_surface *wl_surface;
struct wl_subsurface *wl_subsurface;
struct buffer *buffer;
struct wl_list output_list;
int scale;
} server;
struct {
cairo_surface_t *image;
struct border_component_cairo *parent_component;
} client;
struct wl_list child_components; /* border_component::link */
struct wl_list link; /* border_component::child_components */
};
struct libdecor_frame_cairo {
struct libdecor_frame frame;
struct libdecor_plugin_cairo *plugin_cairo;
int content_width;
int content_height;
enum decoration_type decoration_type;
enum libdecor_window_state window_state;
char *title;
enum libdecor_capabilities capabilities;
struct border_component_cairo *focus;
struct border_component_cairo *active;
struct border_component_cairo *grab;
bool shadow_showing;
struct border_component_cairo shadow;
struct {
bool is_showing;
struct border_component_cairo title;
struct border_component_cairo min;
struct border_component_cairo max;
struct border_component_cairo close;
} title_bar;
/* store pre-processed shadow tile */
cairo_surface_t *shadow_blur;
struct wl_list link;
};
#endif
#if USE_SYSTEM_LIBDECOR || !HAVE_GTK
/* Definitions derived from libdecor-gtk.c */
typedef struct _GtkWidget GtkWidget;
enum header_element { HDR_NONE };
typedef enum { GTK_STATE_FLAG_NORMAL = 0 } GtkStateFlags;
struct border_component_gtk {
enum component type;
struct wl_surface *wl_surface;
struct wl_subsurface *wl_subsurface;
struct buffer *buffer;
bool opaque;
struct wl_list output_list;
int scale;
struct wl_list child_components; /* border_component::link */
struct wl_list link; /* border_component::child_components */
};
struct header_element_data {
const char* name;
enum header_element type;
GtkWidget *widget;
GtkStateFlags state;
};
struct libdecor_frame_gtk {
struct libdecor_frame frame;
struct libdecor_plugin_gtk *plugin_gtk;
int content_width;
int content_height;
enum libdecor_window_state window_state;
enum decoration_type decoration_type;
char *title;
enum libdecor_capabilities capabilities;
struct border_component_gtk *active;
struct border_component_gtk *focus;
struct border_component_gtk *grab;
bool shadow_showing;
struct border_component_gtk shadow;
GtkWidget *window; /* offscreen window for rendering */
GtkWidget *header; /* header bar with widgets */
struct border_component_gtk headerbar;
struct header_element_data hdr_focus;
cairo_surface_t *shadow_blur;
struct wl_list link;
};
#endif // USE_SYSTEM_LIBDECOR || !HAVE_GTK
/* these definitions are copied from libdecor/src/libdecor.c */
struct libdecor_limits {
int min_width;
int min_height;
int max_width;
int max_height;
};
struct libdecor_frame_private {
int ref_count;
struct libdecor *context;
struct wl_surface *wl_surface;
struct libdecor_frame_interface *iface;
void *user_data;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_toplevel_decoration_v1 *toplevel_decoration;
bool pending_map;
struct {
char *app_id;
char *title;
struct libdecor_limits content_limits;
struct xdg_toplevel *parent;
} state;
struct libdecor_configuration *pending_configuration;
int content_width;
int content_height;
enum libdecor_window_state window_state;
enum zxdg_toplevel_decoration_v1_mode decoration_mode;
enum libdecor_capabilities capabilities;
struct libdecor_limits interactive_limits;
bool visible;
};
static unsigned char *gtk_titlebar_buffer(struct libdecor_frame *frame,
int *width, int *height, int *stride)
{
struct libdecor_frame_gtk *lfg = (struct libdecor_frame_gtk *)frame;
#if USE_SYSTEM_LIBDECOR || !HAVE_GTK
struct border_component_gtk *bc;
#else
struct border_component *bc;
#endif
bc = &lfg->headerbar;
struct buffer *buffer = bc->buffer;
*width = buffer->buffer_width;
*height = buffer->buffer_height;
*stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, buffer->buffer_width);
return (unsigned char*)buffer->data;
}
static unsigned char *cairo_titlebar_buffer(struct libdecor_frame *frame,
int *width, int *height, int *stride)
{
struct libdecor_frame_cairo *lfc = (struct libdecor_frame_cairo *)frame;
#if USE_SYSTEM_LIBDECOR || HAVE_GTK
struct border_component_cairo *bc = &lfc->title_bar.title;
#else
struct border_component *bc = &lfc->title_bar.title;
#endif
struct buffer *buffer = bc->server.buffer;
*width = buffer->buffer_width;
*height = buffer->buffer_height;
*stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, buffer->buffer_width);
return (unsigned char*)buffer->data;
}
/*
Although each plugin declares an exported global variable
LIBDECOR_EXPORT const struct libdecor_plugin_description libdecor_plugin_description;
these plugins are dlopen()'ed in libdecor.c without the RTLD_GLOBAL flag.
Consequently their symbols are not discovered by dlsym(RTLD_DEFAULT, "symbol-name").
Under USE_SYSTEM_LIBDECOR, we repeat the dlopen() for the same plugin
then dlsym() will report the address of libdecor_plugin_description.
Under !USE_SYSTEM_LIBDECOR, we compile fl_libdecor.c which modifies the dlopen()
to call dlsym(ld, "libdecor_plugin_description") just after the dlopen and memorizes
this address.
A plugin is loaded also if SSD.
KDE has its own size limit, similar to that of GDK plugin
*/
static const char *get_libdecor_plugin_description(struct libdecor_frame *frame) {
static const struct libdecor_plugin_description *plugin_description = NULL;
if (frame->priv->decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) {
return "Server-Side Decoration";
}
if (!plugin_description) {
#if USE_SYSTEM_LIBDECOR
char fname[PATH_MAX];
const char *dir = getenv("LIBDECOR_PLUGIN_DIR");
if (!dir) dir = LIBDECOR_PLUGIN_DIR;
sprintf(fname, "%s/libdecor-gtk.so", dir);
void *dl = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
if (!dl) {
sprintf(fname, "%s/libdecor-cairo.so", dir);
dl = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
}
if (dl) plugin_description = (const struct libdecor_plugin_description*)dlsym(dl, "libdecor_plugin_description");
#else
plugin_description = fl_libdecor_plugin_description;
extern const struct libdecor_plugin_description libdecor_plugin_description;
if (!plugin_description) plugin_description = &libdecor_plugin_description;
#endif
//if (plugin_description) puts(plugin_description->description);
}
return plugin_description ? plugin_description->description : NULL;
}
/*
FLTK-added utility function to give access to the pixel array representing
the titlebar of a window decorated by the cairo plugin of libdecor.
frame: a libdecor-defined pointer given by fl_xid(win)->frame (with Fl_Window *win);
*width, *height: returned assigned to the width and height in pixels of the titlebar;
*stride: returned assigned to the number of bytes per line of the pixel array;
return value: start of the pixel array, which is in BGRA order, or NULL.
*/
unsigned char *fl_libdecor_titlebar_buffer(struct libdecor_frame *frame,
int *width, int *height, int *stride)
{
static const char *my_plugin = NULL;
if (!my_plugin) my_plugin = get_libdecor_plugin_description(frame);
if (my_plugin && !strcmp(my_plugin, "GTK plugin")) {
return gtk_titlebar_buffer(frame, width, height, stride);
}
else if (my_plugin && !strcmp(my_plugin, "libdecor plugin using Cairo")) {
return cairo_titlebar_buffer(frame, width, height, stride);
}
return NULL;
}

View File

@ -0,0 +1,127 @@
//
// Interface with the libdecor library for the Fast Light Tool Kit (FLTK).
//
// Copyright 2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
/* Improvements to libdecor.c without modifying libdecor.c itself */
#define libdecor_frame_set_minimized libdecor_frame_set_minimized_orig
#define libdecor_new libdecor_new_orig
#include <dlfcn.h>
static void *dlopen_corrected(const char *, int);
#define dlopen(A, B) dlopen_corrected(A, B)
#include "../src/libdecor.c"
#undef dlopen
#undef libdecor_frame_set_minimized
#undef libdecor_new
extern bool fl_libdecor_using_weston(void);
extern const struct libdecor_plugin_description *fl_libdecor_plugin_description;
//#include <stdio.h>
// we have a built-in plugin so don't need a fallback one
struct libdecor_plugin *libdecor_fallback_plugin_new(struct libdecor *context) {
return NULL;
}
// see get_libdecor_plugin_description() explaining why this is useful
static void *dlopen_corrected(const char *filename, int flags) {
static int best_priority = -1;
void *retval = dlopen(filename, flags);
if (retval) {
const struct libdecor_plugin_description *description =
(const struct libdecor_plugin_description*)dlsym(retval, "libdecor_plugin_description");
if (description && description->priorities->priority > best_priority) {
fl_libdecor_plugin_description = description;
best_priority = description->priorities->priority;
}
}
return retval;
}
LIBDECOR_EXPORT void libdecor_frame_set_minimized(struct libdecor_frame *frame)
{
static bool done = false;
static bool using_weston = false;
if (!done) {
typedef bool (*ext_f)(void);
volatile ext_f ext = fl_libdecor_using_weston;
done = true;
if (ext) using_weston = fl_libdecor_using_weston();
//fprintf(stderr, "fl_libdecor_using_weston=%p using_weston=%d\n", fl_libdecor_using_weston, using_weston);
if (using_weston) { // determine the version of the running Weston compositor
FILE *pipe = popen("weston --version", "r");
if (pipe) {
char line[50], *p;
int version = 0;
p = fgets(line, sizeof(line), pipe);
pclose(pipe);
if (p) p = strchr(line, ' ');
if (p) {
sscanf(p, "%d", &version);
// Weston version 10 has fixed the bug handled here
if (version >= 10) using_weston = false;
}
}
}
}
if (using_weston) libdecor_frame_set_visibility(frame, false);
libdecor_frame_set_minimized_orig(frame);
}
/*
By default, FLTK modifies libdecor's libdecor_new() function to determine the plugin as follows :
1) the directory pointed by environment variable LIBDECOR_PLUGIN_DIR or, in absence of this variable,
by -DLIBDECOR_PLUGIN_DIR=xxx at build time is searched for a libdecor plugin;
2) if this directory does not exist or contains no plugin, the built-in plugin is used.
* if FLTK was built with package libgtk-3-dev, the GTK plugin is used
* if FLTK was built without package libgtk-3-dev, the Cairo plugin is used
If FLTK was built with OPTION_USE_SYSTEM_LIBDECOR turned ON, the present modification
isn't compiled, so the plugin-searching algorithm of libdecor_new() in libdecor-0.so is used.
This corresponds to step 1) above and to use no titlebar is no plugin is found.
N.B.: only the system package is built with a meaningful value of -DLIBDECOR_PLUGIN_DIR=
so a plugin may be loaded that way only if FLTK was built with OPTION_USE_SYSTEM_LIBDECOR turned ON.
*/
LIBDECOR_EXPORT struct libdecor *libdecor_new(struct wl_display *wl_display, struct libdecor_interface *iface)
{
struct libdecor *context;
context = zalloc(sizeof *context);
context->ref_count = 1;
context->iface = iface;
context->wl_display = wl_display;
context->wl_registry = wl_display_get_registry(wl_display);
wl_registry_add_listener(context->wl_registry, &registry_listener, context);
context->init_callback = wl_display_sync(context->wl_display);
wl_callback_add_listener(context->init_callback, &init_wl_display_callback_listener, context);
wl_list_init(&context->frames);
// attempt to dynamically load a libdecor plugin with dlopen()
FILE *old_stderr = stderr;
stderr = fopen("/dev/null", "w+"); // avoid "Couldn't open plugin directory" messages
if (init_plugins(context) != 0) { // attempt to load plugin by dlopen()
// no plug-in was found by dlopen(), use built-in plugin instead
// defined in the source code of the built-in plugin: libdecor-cairo.c or libdecor-gtk.c
extern const struct libdecor_plugin_description libdecor_plugin_description;
context->plugin = libdecor_plugin_description.constructor(context);
}
fclose(stderr); // restore stderr as it was before
stderr = old_stderr;
wl_display_flush(wl_display);
return context;
}

1304
libdecor/demo/demo.c Normal file

File diff suppressed because it is too large Load Diff

355
libdecor/demo/egl.c Normal file
View File

@ -0,0 +1,355 @@
/*
* Copyright © 2011 Benjamin Franzke
* Copyright © 2010 Intel Corporation
* Copyright © 2018 Jonas Ådahl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <EGL/egl.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <libdecor.h>
#include <GL/gl.h>
#include <utils.h>
static const size_t default_size = 200;
struct client {
struct wl_display *display;
struct wl_compositor *compositor;
EGLDisplay egl_display;
EGLContext egl_context;
};
struct window {
struct client *client;
struct wl_surface *surface;
struct libdecor_frame *frame;
struct wl_egl_window *egl_window;
EGLSurface egl_surface;
int content_width;
int content_height;
int floating_width;
int floating_height;
bool open;
bool configured;
};
static void
frame_configure(struct libdecor_frame *frame,
struct libdecor_configuration *configuration,
void *user_data)
{
struct window *window = user_data;
struct libdecor_state *state;
int width, height;
if (!libdecor_configuration_get_content_size(configuration, frame,
&width, &height)) {
width = window->floating_width;
height = window->floating_height;
}
window->content_width = width;
window->content_height = height;
wl_egl_window_resize(window->egl_window,
window->content_width, window->content_height,
0, 0);
state = libdecor_state_new(width, height);
libdecor_frame_commit(frame, state, configuration);
libdecor_state_free(state);
/* store floating dimensions */
if (libdecor_frame_is_floating(window->frame)) {
window->floating_width = width;
window->floating_height = height;
}
window->configured = true;
}
static void
frame_close(struct libdecor_frame *frame,
void *user_data)
{
struct window *window = user_data;
window->open = false;
}
static void
frame_commit(struct libdecor_frame *frame,
void *user_data)
{
struct window *window = user_data;
eglSwapBuffers(window->client->display, window->egl_surface);
}
static struct libdecor_frame_interface frame_interface = {
frame_configure,
frame_close,
frame_commit,
};
static void
libdecor_error(struct libdecor *context,
enum libdecor_error error,
const char *message)
{
fprintf(stderr, "Caught error (%d): %s\n", error, message);
exit(EXIT_FAILURE);
}
static struct libdecor_interface libdecor_interface = {
libdecor_error,
};
static void
registry_global(void *data,
struct wl_registry *wl_registry,
uint32_t name,
const char *interface,
uint32_t version)
{
struct client *client = data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
client->compositor = wl_registry_bind(wl_registry, name,
&wl_compositor_interface, 1);
}
}
static void
registry_global_remove(void *data,
struct wl_registry *wl_registry,
uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
registry_global,
registry_global_remove
};
static bool
setup(struct window *window)
{
static const 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_NONE
};
EGLint major, minor;
EGLint n;
EGLConfig config;
window->client->egl_display =
eglGetDisplay((EGLNativeDisplayType)window->client->display);
if (eglInitialize(window->client->egl_display, &major, &minor) == EGL_FALSE) {
fprintf(stderr, "Cannot initialise EGL!\n");
return false;
}
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
fprintf(stderr, "Cannot bind EGL API!\n");
return false;
}
if (eglChooseConfig(window->client->egl_display,
config_attribs,
&config, 1, &n) == EGL_FALSE) {
fprintf(stderr, "No matching EGL configurations!\n");
return false;
}
window->client->egl_context = eglCreateContext(window->client->egl_display,
config, EGL_NO_CONTEXT, NULL);
if (window->client->egl_context == EGL_NO_CONTEXT) {
fprintf(stderr, "No EGL context!\n");
return false;
}
window->surface = wl_compositor_create_surface(window->client->compositor);
window->egl_window = wl_egl_window_create(window->surface,
default_size, default_size);
window->egl_surface = eglCreateWindowSurface(
window->client->egl_display, config,
(EGLNativeWindowType)window->egl_window,
NULL);
eglMakeCurrent(window->client->egl_display, window->egl_surface,
window->egl_surface, window->client->egl_context);
return true;
}
static void
cleanup(struct window *window)
{
if (window->client->egl_display) {
eglMakeCurrent(window->client->egl_display, EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (window->egl_surface) {
eglDestroySurface(window->client->egl_display, window->egl_surface);
}
if (window->egl_window) {
wl_egl_window_destroy(window->egl_window);
}
if (window->surface) {
wl_surface_destroy(window->surface);
}
if (window->client->egl_context) {
eglDestroyContext(window->client->egl_display, window->client->egl_context);
}
if (window->client->egl_display) {
eglTerminate(window->client->egl_display);
}
}
static float
hue_to_channel(const float *const hue, const int n)
{
/* convert hue to rgb channels with saturation and value equal to 1
* https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative
*/
const float k = fmod(n + ((*hue) * 3 / M_PI), 6);
return 1 - MAX(0, MIN(MIN(k, 4 - k), 1));
}
static void
hue_to_rgb(const float *const hue, float (*rgb)[3])
{
(*rgb)[0] = hue_to_channel(hue, 5);
(*rgb)[1] = hue_to_channel(hue, 3);
(*rgb)[2] = hue_to_channel(hue, 1);
}
static void
draw(struct window *window)
{
struct timespec tv;
double time;
/* change of colour hue (HSV space) in rad/sec */
static const float hue_change = (2 * M_PI) / 10;
float hue;
float rgb[3] = {0,0,0};
clock_gettime(CLOCK_REALTIME, &tv);
time = tv.tv_sec + tv.tv_nsec * 1e-9;
hue = fmod(time * hue_change, 2 * M_PI);
hue_to_rgb(&hue, &rgb);
glClearColor(rgb[0], rgb[1], rgb[2], 1);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(window->client->egl_display, window->egl_surface);
}
int
main(int argc, char *argv[])
{
struct wl_registry *wl_registry;
struct libdecor *context = NULL;
struct window *window;
struct client *client;
int ret = EXIT_SUCCESS;
client = calloc(1, sizeof(struct client));
client->display = wl_display_connect(NULL);
if (!client->display) {
fprintf(stderr, "No Wayland connection\n");
free(client);
return EXIT_FAILURE;
}
wl_registry = wl_display_get_registry(client->display);
wl_registry_add_listener(wl_registry, &registry_listener, client);
wl_display_roundtrip(client->display);
window = calloc(1, sizeof(struct window));
window->client = client;
window->open = true;
window->configured = false;
window->floating_width = window->floating_height = default_size;
if (!setup(window)) {
goto out;
}
context = libdecor_new(client->display, &libdecor_interface);
window->frame = libdecor_decorate(context, window->surface,
&frame_interface, window);
libdecor_frame_set_app_id(window->frame, "egl-demo");
libdecor_frame_set_title(window->frame, "EGL demo");
libdecor_frame_map(window->frame);
wl_display_roundtrip(client->display);
wl_display_roundtrip(client->display);
/* wait for the first configure event */
while (!window->configured) {
if (libdecor_dispatch(context, 0) < 0) {
ret = EXIT_FAILURE;
goto out;
}
}
while (window->open) {
if (libdecor_dispatch(context, 0) < 0) {
ret = EXIT_FAILURE;
goto out;
}
draw(window);
}
out:
if (context) {
libdecor_unref(context);
}
cleanup(window);
free(window);
free(client);
return ret;
}

View File

@ -0,0 +1,136 @@
#include "cursor-settings.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "config.h"
#ifdef HAS_DBUS
#include <dbus/dbus.h>
static DBusMessage *
get_setting_sync(DBusConnection *const connection,
const char *key,
const char *value)
{
DBusError error;
dbus_bool_t success;
DBusMessage *message;
DBusMessage *reply;
dbus_error_init(&error);
message = dbus_message_new_method_call(
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Settings",
"Read");
success = dbus_message_append_args(message,
DBUS_TYPE_STRING, &key,
DBUS_TYPE_STRING, &value,
DBUS_TYPE_INVALID);
if (!success)
return NULL;
reply = dbus_connection_send_with_reply_and_block(
connection,
message,
DBUS_TIMEOUT_USE_DEFAULT,
&error);
dbus_message_unref(message);
if (dbus_error_is_set(&error))
return NULL;
return reply;
}
static bool
parse_type(DBusMessage *const reply,
const int type,
void *value)
{
DBusMessageIter iter[3];
dbus_message_iter_init(reply, &iter[0]);
if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT)
return false;
dbus_message_iter_recurse(&iter[0], &iter[1]);
if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT)
return false;
dbus_message_iter_recurse(&iter[1], &iter[2]);
if (dbus_message_iter_get_arg_type(&iter[2]) != type)
return false;
dbus_message_iter_get_basic(&iter[2], value);
return true;
}
bool
libdecor_get_cursor_settings(char **theme, int *size)
{
static const char name[] = "org.gnome.desktop.interface";
static const char key_theme[] = "cursor-theme";
static const char key_size[] = "cursor-size";
DBusError error;
DBusConnection *connection;
DBusMessage *reply;
const char *value_theme = NULL;
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error))
return false;
reply = get_setting_sync(connection, name, key_theme);
if (!reply)
return false;
if (!parse_type(reply, DBUS_TYPE_STRING, &value_theme)) {
dbus_message_unref(reply);
return false;
}
*theme = strdup(value_theme);
dbus_message_unref(reply);
reply = get_setting_sync(connection, name, key_size);
if (!reply)
return false;
if (!parse_type(reply, DBUS_TYPE_INT32, size)) {
dbus_message_unref(reply);
return false;
}
dbus_message_unref(reply);
return true;
}
#else
bool
libdecor_get_cursor_settings(char **theme, int *size)
{
char *env_xtheme;
char *env_xsize;
env_xtheme = getenv("XCURSOR_THEME");
if (env_xtheme != NULL)
*theme = strdup(env_xtheme);
env_xsize = getenv("XCURSOR_SIZE");
if (env_xsize != NULL)
*size = atoi(env_xsize);
return env_xtheme != NULL && env_xsize != NULL;
}
#endif

View File

@ -0,0 +1,6 @@
#pragma once
#include <stdbool.h>
bool
libdecor_get_cursor_settings(char **theme, int *size);

View File

@ -0,0 +1,209 @@
/*
* Copyright © 2019 Jonas Ådahl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "libdecor-fallback.h"
#include <poll.h>
#include <errno.h>
#include "utils.h"
struct libdecor_plugin_fallback {
struct libdecor_plugin plugin;
struct libdecor *context;
};
static void
libdecor_plugin_fallback_destroy(struct libdecor_plugin *plugin)
{
libdecor_plugin_release(plugin);
free(plugin);
}
static int
libdecor_plugin_fallback_get_fd(struct libdecor_plugin *plugin)
{
struct libdecor_plugin_fallback *plugin_fallback =
(struct libdecor_plugin_fallback *) plugin;
struct wl_display *wl_display =
libdecor_get_wl_display(plugin_fallback->context);
return wl_display_get_fd(wl_display);
}
static int
libdecor_plugin_fallback_dispatch(struct libdecor_plugin *plugin,
int timeout)
{
struct libdecor_plugin_fallback *plugin_fallback =
(struct libdecor_plugin_fallback *) plugin;
struct wl_display *wl_display =
libdecor_get_wl_display(plugin_fallback->context);
struct pollfd fds[1];
int ret;
int dispatch_count = 0;
while (wl_display_prepare_read(wl_display) != 0)
dispatch_count += wl_display_dispatch_pending(wl_display);
if (wl_display_flush(wl_display) < 0 &&
errno != EAGAIN) {
wl_display_cancel_read(wl_display);
return -errno;
}
fds[0] = (struct pollfd) { wl_display_get_fd(wl_display), POLLIN };
ret = poll(fds, ARRAY_SIZE (fds), timeout);
if (ret > 0) {
if (fds[0].revents & POLLIN) {
wl_display_read_events(wl_display);
dispatch_count += wl_display_dispatch_pending(wl_display);
return dispatch_count;
} else {
wl_display_cancel_read(wl_display);
return dispatch_count;
}
} else if (ret == 0) {
wl_display_cancel_read(wl_display);
return dispatch_count;
} else {
wl_display_cancel_read(wl_display);
return -errno;
}
}
static struct libdecor_frame *
libdecor_plugin_fallback_frame_new(struct libdecor_plugin *plugin)
{
struct libdecor_frame *frame;
frame = zalloc(sizeof *frame);
return frame;
}
static void
libdecor_plugin_fallback_frame_free(struct libdecor_plugin *plugin,
struct libdecor_frame *frame)
{
}
static void
libdecor_plugin_fallback_frame_commit(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
struct libdecor_state *state,
struct libdecor_configuration *configuration)
{
}
static void
libdecor_plugin_fallback_frame_property_changed(struct libdecor_plugin *plugin,
struct libdecor_frame *frame)
{
}
static void
libdecor_plugin_fallback_frame_translate_coordinate(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
int content_x,
int content_y,
int *frame_x,
int *frame_y)
{
*frame_x = content_x;
*frame_y = content_y;
}
static void
libdecor_plugin_fallback_frame_popup_grab(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
const char *seat_name)
{
}
static void
libdecor_plugin_fallback_frame_popup_ungrab(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
const char *seat_name)
{
}
static bool
libdecor_plugin_fallback_configuration_get_content_size(struct libdecor_plugin *plugin,
struct libdecor_configuration *configuration,
struct libdecor_frame *frame,
int *content_width,
int *content_height)
{
return libdecor_configuration_get_window_size(configuration,
content_width,
content_height);
}
static bool
libdecor_plugin_fallback_frame_get_window_size_for(
struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
struct libdecor_state *state,
int *window_width,
int *window_height)
{
*window_width = libdecor_state_get_content_width (state);
*window_height = libdecor_state_get_content_height (state);
return true;
}
static struct libdecor_plugin_interface fallback_plugin_iface = {
.destroy = libdecor_plugin_fallback_destroy,
.get_fd = libdecor_plugin_fallback_get_fd,
.dispatch = libdecor_plugin_fallback_dispatch,
.frame_new = libdecor_plugin_fallback_frame_new,
.frame_free = libdecor_plugin_fallback_frame_free,
.frame_commit = libdecor_plugin_fallback_frame_commit,
.frame_property_changed = libdecor_plugin_fallback_frame_property_changed,
.frame_translate_coordinate =
libdecor_plugin_fallback_frame_translate_coordinate,
.frame_popup_grab = libdecor_plugin_fallback_frame_popup_grab,
.frame_popup_ungrab = libdecor_plugin_fallback_frame_popup_ungrab,
.configuration_get_content_size = libdecor_plugin_fallback_configuration_get_content_size,
.frame_get_window_size_for = libdecor_plugin_fallback_frame_get_window_size_for,
};
struct libdecor_plugin *
libdecor_fallback_plugin_new(struct libdecor *context)
{
struct libdecor_plugin_fallback *plugin;
plugin = zalloc(sizeof *plugin);
libdecor_plugin_init(&plugin->plugin, context, &fallback_plugin_iface);
plugin->context = context;
libdecor_notify_plugin_ready(context);
return &plugin->plugin;
}

View File

@ -0,0 +1,35 @@
/*
* Copyright © 2019 Jonas Ådahl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBDECOR_FALLBACK_H
#define LIBDECOR_FALLBACK_H
#include "libdecor.h"
#include "libdecor-plugin.h"
struct libdecor_plugin *
libdecor_fallback_plugin_new(struct libdecor *context);
#endif /* LIBDECOR_FALLBACK_H */

View File

@ -0,0 +1,215 @@
/*
* Copyright © 2017-2018 Red Hat Inc.
* Copyright © 2018 Jonas Ådahl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBDECOR_PLUGIN_H
#define LIBDECOR_PLUGIN_H
#include "libdecor.h"
struct libdecor_frame_private;
struct libdecor_frame {
struct libdecor_frame_private *priv;
struct wl_list link;
};
struct libdecor_plugin_private;
struct libdecor_plugin {
struct libdecor_plugin_private *priv;
};
typedef struct libdecor_plugin * (* libdecor_plugin_constructor)(struct libdecor *context);
#define LIBDECOR_PLUGIN_PRIORITY_HIGH 1000
#define LIBDECOR_PLUGIN_PRIORITY_MEDIUM 100
#define LIBDECOR_PLUGIN_PRIORITY_LOW 0
struct libdecor_plugin_priority {
const char *desktop;
int priority;
};
enum libdecor_plugin_capabilities {
LIBDECOR_PLUGIN_CAPABILITY_BASE = 1 << 0,
};
struct libdecor_plugin_description {
/* API version the plugin is compatible with. */
int api_version;
/* Human readable string describing the plugin. */
char *description;
/* A plugin has a bitmask of capabilities. The plugin loader can use this
* to load a plugin with the right capabilities. */
enum libdecor_plugin_capabilities capabilities;
/*
* The priorities field points to a list of per desktop priorities.
* properties[i].desktop is matched against XDG_CURRENT_DESKTOP when
* determining what plugin to use. The last entry in the list MUST have
* the priorities[i].desktop pointer set to NULL as a default
* priority.
*/
const struct libdecor_plugin_priority *priorities;
/* Vfunc used for constructing a plugin instance. */
libdecor_plugin_constructor constructor;
};
struct libdecor_plugin_interface {
void (* destroy)(struct libdecor_plugin *plugin);
int (* get_fd)(struct libdecor_plugin *plugin);
int (* dispatch)(struct libdecor_plugin *plugin,
int timeout);
struct libdecor_frame * (* frame_new)(struct libdecor_plugin *plugin);
void (* frame_free)(struct libdecor_plugin *plugin,
struct libdecor_frame *frame);
void (* frame_commit)(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
struct libdecor_state *state,
struct libdecor_configuration *configuration);
void (*frame_property_changed)(struct libdecor_plugin *plugin,
struct libdecor_frame *frame);
void (* frame_translate_coordinate)(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
int content_x,
int content_y,
int *window_x,
int *window_y);
void (* frame_popup_grab)(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
const char *seat_name);
void (* frame_popup_ungrab)(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
const char *seat_name);
bool (* frame_get_window_size_for)(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
struct libdecor_state *state,
int *window_width,
int *window_height);
bool (* configuration_get_content_size)(struct libdecor_plugin *plugin,
struct libdecor_configuration *configuration,
struct libdecor_frame *frame,
int *content_width,
int *content_height);
/* Reserved */
void (* reserved0)(void);
void (* reserved1)(void);
void (* reserved2)(void);
void (* reserved3)(void);
void (* reserved4)(void);
void (* reserved5)(void);
void (* reserved6)(void);
void (* reserved7)(void);
void (* reserved8)(void);
void (* reserved9)(void);
};
struct wl_surface *
libdecor_frame_get_wl_surface(struct libdecor_frame *frame);
int
libdecor_frame_get_content_width(struct libdecor_frame *frame);
int
libdecor_frame_get_content_height(struct libdecor_frame *frame);
enum libdecor_window_state
libdecor_frame_get_window_state(struct libdecor_frame *frame);
void
libdecor_frame_set_window_geometry(struct libdecor_frame *frame,
int32_t x, int32_t y,
int32_t width, int32_t height);
enum libdecor_capabilities
libdecor_frame_get_capabilities(const struct libdecor_frame *frame);
void
libdecor_frame_dismiss_popup(struct libdecor_frame *frame,
const char *seat_name);
void
libdecor_frame_toplevel_commit(struct libdecor_frame *frame);
struct wl_display *
libdecor_get_wl_display(struct libdecor *context);
void
libdecor_notify_plugin_ready(struct libdecor *context);
void
libdecor_notify_plugin_error(struct libdecor *context,
enum libdecor_error error,
const char *__restrict fmt,
...);
int
libdecor_state_get_content_width (struct libdecor_state *state);
int
libdecor_state_get_content_height (struct libdecor_state *state);
enum libdecor_window_state
libdecor_state_get_window_state(struct libdecor_state *state);
bool
libdecor_configuration_get_window_size(struct libdecor_configuration *configuration,
int *width,
int *height);
int
libdecor_plugin_init(struct libdecor_plugin *plugin,
struct libdecor *context,
struct libdecor_plugin_interface *iface);
void
libdecor_plugin_release(struct libdecor_plugin *plugin);
/*
* Get the min content size as set before with libdecor_frame_set_min_content_size().
*/
void
libdecor_frame_get_min_content_size(struct libdecor_frame *frame,
int *pcontent_width,
int *pcontent_height);
/*
* Get the max content size as set before with libdecor_frame_set_max_content_size().
*/
void
libdecor_frame_get_max_content_size(struct libdecor_frame *frame,
int *pcontent_width,
int *pcontent_height);
#endif /* LIBDECOR_PLUGIN_H */

1664
libdecor/src/libdecor.c Normal file

File diff suppressed because it is too large Load Diff

521
libdecor/src/libdecor.h Normal file
View File

@ -0,0 +1,521 @@
/*
* Copyright © 2017-2018 Red Hat Inc.
* Copyright © 2018 Jonas Ådahl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBDECOR_H
#define LIBDECOR_H
#include <stdbool.h>
#include <wayland-client.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) && __GNUC__ >= 4
#define LIBDECOR_EXPORT __attribute__ ((visibility("default")))
#else
#define LIBDECOR_EXPORT
#endif
struct xdg_toplevel;
/** \class libdecor
*
* \brief A libdecor context instance.
*/
struct libdecor;
/** \class libdecor_frame
*
* \brief A frame used for decorating a Wayland surface.
*/
struct libdecor_frame;
/** \class libdecor_configuration
*
* \brief An object representing a toplevel window configuration.
*/
struct libdecor_configuration;
/** \class libdecor_state
*
* \brief An object corresponding to a configured content state.
*/
struct libdecor_state;
enum libdecor_error {
LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE,
LIBDECOR_ERROR_INVALID_FRAME_CONFIGURATION,
};
enum libdecor_window_state {
LIBDECOR_WINDOW_STATE_NONE = 0,
LIBDECOR_WINDOW_STATE_ACTIVE = 1 << 0,
LIBDECOR_WINDOW_STATE_MAXIMIZED = 1 << 1,
LIBDECOR_WINDOW_STATE_FULLSCREEN = 1 << 2,
LIBDECOR_WINDOW_STATE_TILED_LEFT = 1 << 3,
LIBDECOR_WINDOW_STATE_TILED_RIGHT = 1 << 4,
LIBDECOR_WINDOW_STATE_TILED_TOP = 1 << 5,
LIBDECOR_WINDOW_STATE_TILED_BOTTOM = 1 << 6,
};
enum libdecor_resize_edge {
LIBDECOR_RESIZE_EDGE_NONE,
LIBDECOR_RESIZE_EDGE_TOP,
LIBDECOR_RESIZE_EDGE_BOTTOM,
LIBDECOR_RESIZE_EDGE_LEFT,
LIBDECOR_RESIZE_EDGE_TOP_LEFT,
LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT,
LIBDECOR_RESIZE_EDGE_RIGHT,
LIBDECOR_RESIZE_EDGE_TOP_RIGHT,
LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT,
};
enum libdecor_capabilities {
LIBDECOR_ACTION_MOVE = 1 << 0,
LIBDECOR_ACTION_RESIZE = 1 << 1,
LIBDECOR_ACTION_MINIMIZE = 1 << 2,
LIBDECOR_ACTION_FULLSCREEN = 1 << 3,
LIBDECOR_ACTION_CLOSE = 1 << 4,
};
struct libdecor_interface {
/**
* An error event
*/
void (* error)(struct libdecor *context,
enum libdecor_error error,
const char *message);
/* Reserved */
void (* reserved0)(void);
void (* reserved1)(void);
void (* reserved2)(void);
void (* reserved3)(void);
void (* reserved4)(void);
void (* reserved5)(void);
void (* reserved6)(void);
void (* reserved7)(void);
void (* reserved8)(void);
void (* reserved9)(void);
};
/**
* Interface for integrating a Wayland surface with libdecor.
*/
struct libdecor_frame_interface {
/**
* A new configuration was received. An application should respond to
* this by creating a suitable libdecor_state, and apply it using
* libdecor_frame_commit.
*/
void (* configure)(struct libdecor_frame *frame,
struct libdecor_configuration *configuration,
void *user_data);
/**
* The window was requested to be closed by the compositor.
*/
void (* close)(struct libdecor_frame *frame,
void *user_data);
/**
* The window decoration asked to have the main surface to be
* committed. This is required when the decoration is implemented using
* synchronous subsurfaces.
*/
void (* commit)(struct libdecor_frame *frame,
void *user_data);
/**
* Any mapped popup that has a grab on the given seat should be
* dismissed.
*/
void (* dismiss_popup)(struct libdecor_frame *frame,
const char *seat_name,
void *user_data);
/* Reserved */
void (* reserved0)(void);
void (* reserved1)(void);
void (* reserved2)(void);
void (* reserved3)(void);
void (* reserved4)(void);
void (* reserved5)(void);
void (* reserved6)(void);
void (* reserved7)(void);
void (* reserved8)(void);
void (* reserved9)(void);
};
/**
* Remove a reference to the libdecor instance. When the reference count
* reaches zero, it is freed.
*/
void
libdecor_unref(struct libdecor *context);
/**
* Create a new libdecor context for the given wl_display.
*/
struct libdecor *
libdecor_new(struct wl_display *display,
struct libdecor_interface *iface);
/**
* Get the file descriptor used by libdecor. This is similar to
* wl_display_get_fd(), thus should be polled, and when data is available,
* libdecor_dispatch() should be called.
*/
int
libdecor_get_fd(struct libdecor *context);
/**
* Dispatch events. This function should be called when data is available on
* the file descriptor returned by libdecor_get_fd(). If timeout is zero, this
* function will never block.
*/
int
libdecor_dispatch(struct libdecor *context,
int timeout);
/**
* Decorate the given content wl_surface.
*
* This will create an xdg_surface and an xdg_toplevel, and integrate it
* properly with the windowing system, including creating appropriate
* decorations when needed, as well as handle windowing integration events such
* as resizing, moving, maximizing, etc.
*
* The passed wl_surface should only contain actual application content,
* without any window decoration.
*/
struct libdecor_frame *
libdecor_decorate(struct libdecor *context,
struct wl_surface *surface,
struct libdecor_frame_interface *iface,
void *user_data);
/**
* Add a reference to the frame object.
*/
void
libdecor_frame_ref(struct libdecor_frame *frame);
/**
* Remove a reference to the frame object. When the reference count reaches
* zero, the frame object is destroyed.
*/
void
libdecor_frame_unref(struct libdecor_frame *frame);
/**
* Set the visibility of the frame.
*
* If an application wants to be borderless, it can set the frame visibility to
* false.
*/
void
libdecor_frame_set_visibility(struct libdecor_frame *frame,
bool visible);
/**
* Get the visibility of the frame.
*/
bool
libdecor_frame_is_visible(struct libdecor_frame *frame);
/**
* Set the parent of the window.
*
* This can be used to stack multiple toplevel windows above or under each
* other.
*/
void
libdecor_frame_set_parent(struct libdecor_frame *frame,
struct libdecor_frame *parent);
/**
* Set the title of the window.
*/
void
libdecor_frame_set_title(struct libdecor_frame *frame,
const char *title);
/**
* Get the title of the window.
*/
const char *
libdecor_frame_get_title(struct libdecor_frame *frame);
/**
* Set the application ID of the window.
*/
void
libdecor_frame_set_app_id(struct libdecor_frame *frame,
const char *app_id);
/**
* Set new capabilities of the window.
*
* This determines whether e.g. a window decoration should show a maximize
* button, etc.
*
* Setting a capability does not implicitly unset any other.
*/
void
libdecor_frame_set_capabilities(struct libdecor_frame *frame,
enum libdecor_capabilities capabilities);
/**
* Unset capabilities of the window.
*
* The opposite of libdecor_frame_set_capabilities.
*/
void
libdecor_frame_unset_capabilities(struct libdecor_frame *frame,
enum libdecor_capabilities capabilities);
/**
* Check whether the window has any of the given capabilities.
*/
bool
libdecor_frame_has_capability(struct libdecor_frame *frame,
enum libdecor_capabilities capability);
/**
* Show the window menu.
*/
void
libdecor_frame_show_window_menu(struct libdecor_frame *frame,
struct wl_seat *wl_seat,
uint32_t serial,
int x,
int y);
/**
* Issue a popup grab on the window. Call this when a xdg_popup is mapped, so
* that it can be properly dismissed by the decorations.
*/
void
libdecor_frame_popup_grab(struct libdecor_frame *frame,
const char *seat_name);
/**
* Release the popup grab. Call this when you unmap a popup.
*/
void
libdecor_frame_popup_ungrab(struct libdecor_frame *frame,
const char *seat_name);
/**
* Translate content surface local coordinates to toplevel window local
* coordinates.
*
* This can be used to translate surface coordinates to coordinates useful for
* e.g. showing the window menu, or positioning a popup.
*/
void
libdecor_frame_translate_coordinate(struct libdecor_frame *frame,
int surface_x,
int surface_y,
int *frame_x,
int *frame_y);
/**
* Set the max content size.
*
* This translates roughly to xdg_toplevel_set_max_size().
*/
void
libdecor_frame_set_max_content_size(struct libdecor_frame *frame,
int content_width,
int content_height);
/**
* Set the min content size.
*
* This translates roughly to xdg_toplevel_set_min_size().
*/
void
libdecor_frame_set_min_content_size(struct libdecor_frame *frame,
int content_width,
int content_height);
/**
* Initiate an interactive resize.
*
* This roughly translates to xdg_toplevel_resize().
*/
void
libdecor_frame_resize(struct libdecor_frame *frame,
struct wl_seat *wl_seat,
uint32_t serial,
enum libdecor_resize_edge edge);
/**
* Initiate an interactive move.
*
* This roughly translates to xdg_toplevel_move().
*/
void
libdecor_frame_move(struct libdecor_frame *frame,
struct wl_seat *wl_seat,
uint32_t serial);
/**
* Commit a new window state. This can be called on application driven resizes
* when the window is floating, or in response to received configurations, i.e.
* from e.g. interactive resizes or state changes.
*/
void
libdecor_frame_commit(struct libdecor_frame *frame,
struct libdecor_state *state,
struct libdecor_configuration *configuration);
/**
* Minimize the window.
*
* Roughly translates to xdg_toplevel_set_minimized().
*/
void
libdecor_frame_set_minimized(struct libdecor_frame *frame);
/**
* Maximize the window.
*
* Roughly translates to xdg_toplevel_set_maximized().
*/
void
libdecor_frame_set_maximized(struct libdecor_frame *frame);
/**
* Unmaximize the window.
*
* Roughly translates to xdg_toplevel_unset_maximized().
*/
void
libdecor_frame_unset_maximized(struct libdecor_frame *frame);
/**
* Fullscreen the window.
*
* Roughly translates to xdg_toplevel_set_fullscreen().
*/
void
libdecor_frame_set_fullscreen(struct libdecor_frame *frame,
struct wl_output *output);
/**
* Unfullscreen the window.
*
* Roughly translates to xdg_toplevel_unset_unfullscreen().
*/
void
libdecor_frame_unset_fullscreen(struct libdecor_frame *frame);
/**
* Return true if the window is floating.
*
* A window is floating when it's not maximized, tiled, fullscreen, or in any
* similar way with a fixed size and state.
* Note that this function uses the "applied" configuration. If this function
* is used in the 'configure' callback, the provided configuration has to be
* applied via 'libdecor_frame_commit' first, before it will reflect the current
* window state from the provided configuration.
*/
bool
libdecor_frame_is_floating(struct libdecor_frame *frame);
/**
* Close the window.
*
* Roughly translates to xdg_toplevel_close().
*/
void
libdecor_frame_close(struct libdecor_frame *frame);
/**
* Map the window.
*
* This will eventually result in the initial configure event.
*/
void
libdecor_frame_map(struct libdecor_frame *frame);
/**
* Get the associated xdg_surface for content wl_surface.
*/
struct xdg_surface *
libdecor_frame_get_xdg_surface(struct libdecor_frame *frame);
/**
* Get the associated xdg_toplevel for the content wl_surface.
*/
struct xdg_toplevel *
libdecor_frame_get_xdg_toplevel(struct libdecor_frame *frame);
/**
* Create a new content surface state.
*/
struct libdecor_state *
libdecor_state_new(int width,
int height);
/**
* Free a content surface state.
*/
void
libdecor_state_free(struct libdecor_state *state);
/**
* Get the expected size of the content for this configuration.
*
* If the configuration doesn't contain a size, false is returned.
*/
bool
libdecor_configuration_get_content_size(struct libdecor_configuration *configuration,
struct libdecor_frame *frame,
int *width,
int *height);
/**
* Get the window state for this configuration.
*
* If the configuration doesn't contain any associated window state, false is
* returned, and the application should assume the window state remains
* unchanged.
*/
bool
libdecor_configuration_get_window_state(struct libdecor_configuration *configuration,
enum libdecor_window_state *window_state);
#ifdef __cplusplus
}
#endif
#endif /* LIBDECOR_H */

View File

@ -0,0 +1,181 @@
/*
* Copyright © 2012 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_MEMFD_CREATE
#include <sys/mman.h>
#endif
#include "os-compatibility.h"
#ifndef HAVE_MKOSTEMP
static int
set_cloexec_or_close(int fd)
{
long flags;
if (fd == -1)
return -1;
flags = fcntl(fd, F_GETFD);
if (flags == -1)
goto err;
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
goto err;
return fd;
err:
close(fd);
return -1;
}
#endif
static int
create_tmpfile_cloexec(char *tmpname)
{
int fd;
#ifdef HAVE_MKOSTEMP
fd = mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0)
unlink(tmpname);
#else
fd = mkstemp(tmpname);
if (fd >= 0) {
fd = set_cloexec_or_close(fd);
unlink(tmpname);
}
#endif
return fd;
}
static int
os_resize_anonymous_file(int fd, off_t size)
{
#ifdef HAVE_POSIX_FALLOCATE
/*
* Filesystems that do support fallocate will return EINVAL or
* EOPNOTSUPP. In this case we need to fall back to ftruncate
*/
errno = posix_fallocate(fd, 0, size);
if (errno == 0)
return 0;
else if (errno != EINVAL && errno != EOPNOTSUPP)
return -1;
#endif
if (ftruncate(fd, size) < 0)
return -1;
return 0;
}
/*
* Create a new, unique, anonymous file of the given size, and
* return the file descriptor for it. The file descriptor is set
* CLOEXEC. The file is immediately suitable for mmap()'ing
* the given size at offset zero.
*
* The file should not have a permanent backing store like a disk,
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
*
* The file name is deleted from the file system.
*
* The file is suitable for buffer sharing between processes by
* transmitting the file descriptor over Unix sockets using the
* SCM_RIGHTS methods.
*
* If the C library implements posix_fallocate(), it is used to
* guarantee that disk space is available for the file at the
* given size. If disk space is insufficient, errno is set to ENOSPC.
* If posix_fallocate() is not supported, program may receive
* SIGBUS on accessing mmap()'ed file contents instead.
*
* If the C library implements memfd_create(), it is used to create the
* file purely in memory, without any backing file name on the file
* system, and then sealing off the possibility of shrinking it. This
* can then be checked before accessing mmap()'ed file contents, to
* make sure SIGBUS can't happen. It also avoids requiring
* XDG_RUNTIME_DIR.
*/
int
os_create_anonymous_file(off_t size)
{
static const char template[] = "/libdecor-shared-XXXXXX";
const char *path;
char *name;
int fd;
#ifdef HAVE_MEMFD_CREATE
fd = memfd_create("libdecor", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd >= 0) {
/* We can add this seal before calling posix_fallocate(), as
* the file is currently zero-sized anyway.
*
* There is also no need to check for the return value, we
* couldn't do anything with it anyway.
*/
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
} else
#endif
{
path = getenv("XDG_RUNTIME_DIR");
if (!path) {
errno = ENOENT;
return -1;
}
name = malloc(strlen(path) + sizeof(template));
if (!name)
return -1;
strcpy(name, path);
strcat(name, template);
fd = create_tmpfile_cloexec(name);
free(name);
if (fd < 0)
return -1;
}
if (os_resize_anonymous_file(fd, size) < 0) {
close(fd);
return -1;
}
return fd;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright © 2012 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef OS_COMPATIBILITY_H
#define OS_COMPATIBILITY_H
#include <sys/types.h>
int
os_create_anonymous_file(off_t size);
#endif /* OS_COMPATIBILITY_H */

View File

@ -0,0 +1,255 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2012 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* functions 'blur_surface' and 'render_shadow' from weston project:
* https://gitlab.freedesktop.org/wayland/weston/raw/master/shared/cairo-util.c
*/
#include "libdecor-cairo-blur.h"
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
/**
* Compile-time computation of number of items in a hardcoded array.
*
* @param a the array being measured.
* @return the number of items hardcoded into the array.
*/
#ifndef ARRAY_LENGTH
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#endif
int
blur_surface(cairo_surface_t *surface, int margin)
{
int32_t width, height, stride, x, y, z, w;
uint8_t *src, *dst;
uint32_t *s, *d, a, p;
int i, j, k, size, half;
uint32_t kernel[71];
double f;
size = ARRAY_LENGTH(kernel);
width = cairo_image_surface_get_width(surface);
height = cairo_image_surface_get_height(surface);
stride = cairo_image_surface_get_stride(surface);
src = cairo_image_surface_get_data(surface);
dst = malloc(height * stride);
if (dst == NULL)
return -1;
half = size / 2;
a = 0;
for (i = 0; i < size; i++) {
f = (i - half);
kernel[i] = exp(- f * f / ARRAY_LENGTH(kernel)) * 10000;
a += kernel[i];
}
for (i = 0; i < height; i++) {
s = (uint32_t *) (src + i * stride);
d = (uint32_t *) (dst + i * stride);
for (j = 0; j < width; j++) {
if (margin < j && j < width - margin) {
d[j] = s[j];
continue;
}
x = 0;
y = 0;
z = 0;
w = 0;
for (k = 0; k < size; k++) {
if (j - half + k < 0 || j - half + k >= width)
continue;
p = s[j - half + k];
x += (p >> 24) * kernel[k];
y += ((p >> 16) & 0xff) * kernel[k];
z += ((p >> 8) & 0xff) * kernel[k];
w += (p & 0xff) * kernel[k];
}
d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
}
}
for (i = 0; i < height; i++) {
s = (uint32_t *) (dst + i * stride);
d = (uint32_t *) (src + i * stride);
for (j = 0; j < width; j++) {
if (margin <= i && i < height - margin) {
d[j] = s[j];
continue;
}
x = 0;
y = 0;
z = 0;
w = 0;
for (k = 0; k < size; k++) {
if (i - half + k < 0 || i - half + k >= height)
continue;
s = (uint32_t *) (dst + (i - half + k) * stride);
p = s[j];
x += (p >> 24) * kernel[k];
y += ((p >> 16) & 0xff) * kernel[k];
z += ((p >> 8) & 0xff) * kernel[k];
w += (p & 0xff) * kernel[k];
}
d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
}
}
free(dst);
cairo_surface_mark_dirty(surface);
return 0;
}
void
render_shadow(cairo_t *cr, cairo_surface_t *surface,
int x, int y, int width, int height, int margin, int top_margin)
{
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
int i, fx, fy, shadow_height, shadow_width;
cairo_set_source_rgba(cr, 0, 0, 0, 0.45);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
for (i = 0; i < 4; i++) {
/* when fy is set, then we are working with lower corners,
* when fx is set, then we are working with right corners
*
* 00 ------- 01
* | |
* | |
* 10 ------- 11
*/
fx = i & 1;
fy = i >> 1;
cairo_matrix_init_translate(&matrix,
-x + fx * (128 - width),
-y + fy * (128 - height));
cairo_pattern_set_matrix(pattern, &matrix);
shadow_width = margin;
shadow_height = fy ? margin : top_margin;
/* if the shadows together are greater than the surface, we need
* to fix it - set the shadow size to the half of
* the size of surface. Also handle the case when the size is
* not divisible by 2. In that case we need one part of the
* shadow to be one pixel greater. !fy or !fx, respectively,
* will do the work.
*/
if (height < 2 * shadow_height)
shadow_height = (height + !fy) / 2;
if (width < 2 * shadow_width)
shadow_width = (width + !fx) / 2;
cairo_reset_clip(cr);
cairo_rectangle(cr,
x + fx * (width - shadow_width),
y + fy * (height - shadow_height),
shadow_width, shadow_height);
cairo_clip (cr);
cairo_mask(cr, pattern);
}
shadow_width = width - 2 * margin;
shadow_height = top_margin;
if (height < 2 * shadow_height)
shadow_height = height / 2;
if (shadow_width > 0 && shadow_height) {
/* Top stretch */
cairo_matrix_init_translate(&matrix, 60, 0);
cairo_matrix_scale(&matrix, 8.0 / width, 1);
cairo_matrix_translate(&matrix, -x - width / 2, -y);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_rectangle(cr, x + margin, y, shadow_width, shadow_height);
cairo_reset_clip(cr);
cairo_rectangle(cr,
x + margin, y,
shadow_width, shadow_height);
cairo_clip (cr);
cairo_mask(cr, pattern);
/* Bottom stretch */
cairo_matrix_translate(&matrix, 0, -height + 128);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_reset_clip(cr);
cairo_rectangle(cr, x + margin, y + height - margin,
shadow_width, margin);
cairo_clip (cr);
cairo_mask(cr, pattern);
}
shadow_width = margin;
if (width < 2 * shadow_width)
shadow_width = width / 2;
shadow_height = height - margin - top_margin;
/* if height is smaller than sum of margins,
* then the shadow is already done by the corners */
if (shadow_height > 0 && shadow_width) {
/* Left stretch */
cairo_matrix_init_translate(&matrix, 0, 60);
cairo_matrix_scale(&matrix, 1, 8.0 / height);
cairo_matrix_translate(&matrix, -x, -y - height / 2);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_reset_clip(cr);
cairo_rectangle(cr, x, y + top_margin,
shadow_width, shadow_height);
cairo_clip (cr);
cairo_mask(cr, pattern);
/* Right stretch */
cairo_matrix_translate(&matrix, -width + 128, 0);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_rectangle(cr, x + width - shadow_width, y + top_margin,
shadow_width, shadow_height);
cairo_reset_clip(cr);
cairo_clip (cr);
cairo_mask(cr, pattern);
}
cairo_pattern_destroy(pattern);
cairo_reset_clip(cr);
}

View File

@ -0,0 +1,10 @@
#pragma once
#include <cairo/cairo.h>
int
blur_surface(cairo_surface_t *surface, int margin);
void
render_shadow(cairo_t *cr, cairo_surface_t *surface,
int x, int y, int width, int height, int margin, int top_margin);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
/*
* Copyright © 2021 Jonas Ådahl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "libdecor-plugin.h"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdlib.h>
#include <wayland-cursor.h>
#include "utils.h"
struct libdecor_plugin_dummy {
struct libdecor_plugin plugin;
struct libdecor *context;
};
static void
libdecor_plugin_dummy_destroy(struct libdecor_plugin *plugin)
{
struct libdecor_plugin_dummy *plugin_dummy =
(struct libdecor_plugin_dummy *) plugin;
free(plugin_dummy);
}
static struct libdecor_frame *
libdecor_plugin_dummy_frame_new(struct libdecor_plugin *plugin)
{
struct libdecor_frame *frame;
frame = zalloc(sizeof *frame);
return frame;
}
static void
libdecor_plugin_dummy_frame_free(struct libdecor_plugin *plugin,
struct libdecor_frame *frame)
{
}
static void
libdecor_plugin_dummy_frame_commit(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
struct libdecor_state *state,
struct libdecor_configuration *configuration)
{
}
static void
libdecor_plugin_dummy_frame_property_changed(struct libdecor_plugin *plugin,
struct libdecor_frame *frame)
{
}
static void
libdecor_plugin_dummy_frame_translate_coordinate(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
int content_x,
int content_y,
int *frame_x,
int *frame_y)
{
*frame_x = content_x;
*frame_y = content_y;
}
static void
libdecor_plugin_dummy_frame_popup_grab(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
const char *seat_name)
{
}
static void
libdecor_plugin_dummy_frame_popup_ungrab(struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
const char *seat_name)
{
}
static bool
libdecor_plugin_dummy_configuration_get_content_size(
struct libdecor_plugin *plugin,
struct libdecor_configuration *configuration,
struct libdecor_frame *frame,
int *content_width,
int *content_height)
{
return libdecor_configuration_get_window_size(configuration,
content_width,
content_height);
}
static bool
libdecor_plugin_dummy_frame_get_window_size_for(
struct libdecor_plugin *plugin,
struct libdecor_frame *frame,
struct libdecor_state *state,
int *window_width,
int *window_height)
{
*window_width = libdecor_state_get_content_width (state);
*window_height = libdecor_state_get_content_height (state);
return true;
}
static struct libdecor_plugin_interface dummy_plugin_iface = {
.destroy = libdecor_plugin_dummy_destroy,
.frame_new = libdecor_plugin_dummy_frame_new,
.frame_free = libdecor_plugin_dummy_frame_free,
.frame_commit = libdecor_plugin_dummy_frame_commit,
.frame_property_changed = libdecor_plugin_dummy_frame_property_changed,
.frame_translate_coordinate =
libdecor_plugin_dummy_frame_translate_coordinate,
.frame_popup_grab = libdecor_plugin_dummy_frame_popup_grab,
.frame_popup_ungrab = libdecor_plugin_dummy_frame_popup_ungrab,
.configuration_get_content_size =
libdecor_plugin_dummy_configuration_get_content_size,
.frame_get_window_size_for =
libdecor_plugin_dummy_frame_get_window_size_for,
};
static struct libdecor_plugin *
libdecor_plugin_new(struct libdecor *context)
{
struct libdecor_plugin_dummy *plugin_dummy;
plugin_dummy = zalloc(sizeof *plugin_dummy);
plugin_dummy->plugin.iface = &dummy_plugin_iface;
plugin_dummy->context = context;
libdecor_notify_plugin_ready(context);
return &plugin_dummy->plugin;
}
static struct libdecor_plugin_priority priorities[] = {
{ NULL, LIBDECOR_PLUGIN_PRIORITY_LOW }
};
LIBDECOR_EXPORT const struct libdecor_plugin_description
libdecor_plugin_description = {
.api_version = LIBDECOR_PLUGIN_API_VERSION,
.description = "dummy libdecor plugin",
.priorities = priorities,
.constructor = libdecor_plugin_new,
};

File diff suppressed because it is too large Load Diff

48
libdecor/src/utils.h Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright © 2017 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef UTILS_H
#define UTILS_H
#include <stdlib.h>
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#ifndef ARRAY_LENGTH
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
static inline void *
zalloc(size_t size)
{
return calloc(1, size);
}
#endif /* UTILS_H */

View File

@ -1,7 +1,7 @@
#
# Make include file for the Fast Light Tool Kit (FLTK).
#
# Copyright 1998-2021 by Bill Spitzak and others.
# 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
@ -24,7 +24,7 @@ FL_ABI_VERSION = @FL_ABI_VERSION@
FLTK_VERSION = @FLTK_VERSION@
# FLTK configuration options: BUILD = { WIN | OSX | X11 | XFT }
# FLTK configuration options: BUILD = { WIN | OSX | X11 | XFT | WAYLAND }
BUILD = @BUILD@
@ -105,6 +105,9 @@ FLTKCAIROOPTION = @FLTKCAIROOPTION@
LINKSHARED = @DSOLINK@ @LINKSHARED@ $(IMAGELIBS) $(CAIROLIBS)
IMAGELIBS = -L../lib @IMAGELIBS@
# optional extra build step for libdecor:
LIBDECORDIR = @LIBDECORDIR@
# image libraries to build...
IMAGEDIRS = @JPEG@ @ZLIB@ @PNG@
CAIRODIR = @CAIRODIR@

View File

@ -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}")

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -78,155 +78,16 @@ static bool have_xfixes = false;
# include <X11/extensions/Xrender.h>
# endif
# if USE_POLL
# include <poll.h>
# else
# define POLLIN 1
# endif /* USE_POLL */
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);
}
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
}
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()) {

View File

@ -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) \

View File

@ -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 */

View File

@ -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;
}

View File

@ -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_

View File

@ -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();
}

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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;
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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);

View File

@ -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)