1998-10-20 00:46:58 +04:00
|
|
|
//
|
|
|
|
// X specific code for the Fast Light Tool Kit (FLTK).
|
|
|
|
//
|
2024-05-10 21:26:53 +03:00
|
|
|
// Copyright 1998-2024 by Bill Spitzak and others.
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
2011-07-19 08:49:30 +04:00
|
|
|
// 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:
|
|
|
|
//
|
2020-02-10 15:38:26 +03:00
|
|
|
// https://www.fltk.org/COPYING.php
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
2020-07-01 19:03:10 +03:00
|
|
|
// Please see the following page on how to report bugs and issues:
|
2005-04-15 22:00:33 +04:00
|
|
|
//
|
2020-07-01 19:03:10 +03:00
|
|
|
// https://www.fltk.org/bugs.php
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2023-05-09 23:36:41 +03:00
|
|
|
// Note: this file contains platform specific code and will therefore
|
|
|
|
// not be processed by doxygen (see Doxyfile.in).
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2021-11-05 14:46:21 +03:00
|
|
|
# ifndef FLTK_CONSOLIDATE_MOTION
|
|
|
|
# define FLTK_CONSOLIDATE_MOTION 0
|
|
|
|
# endif
|
|
|
|
|
1999-03-09 20:08:35 +03:00
|
|
|
/**** Define this if your keyboard lacks a backspace key... ****/
|
|
|
|
/* #define BACKSPACE_HACK 1 */
|
1998-12-07 16:34:27 +03:00
|
|
|
|
2008-09-11 03:56:49 +04:00
|
|
|
# include <config.h>
|
2001-11-27 20:44:08 +03:00
|
|
|
# include <FL/Fl.H>
|
2018-02-01 00:17:17 +03:00
|
|
|
# include <FL/platform.H>
|
2018-06-26 16:43:18 +03:00
|
|
|
# include "Fl_Window_Driver.H"
|
2001-11-27 20:44:08 +03:00
|
|
|
# include <FL/Fl_Window.H>
|
2008-09-19 21:40:20 +04:00
|
|
|
# include <FL/fl_utf8.h>
|
2006-06-14 14:48:36 +04:00
|
|
|
# include <FL/Fl_Tooltip.H>
|
2008-12-30 18:03:13 +03:00
|
|
|
# include <FL/fl_draw.H>
|
2011-04-16 01:38:05 +04:00
|
|
|
# include <FL/Fl_Paged_Device.H>
|
2014-05-23 20:47:21 +04:00
|
|
|
# include <FL/Fl_Shared_Image.H>
|
2014-02-07 04:09:52 +04:00
|
|
|
# include <FL/fl_ask.H>
|
2014-08-21 16:29:48 +04:00
|
|
|
# include <FL/filename.H>
|
2001-11-27 20:44:08 +03:00
|
|
|
# include <stdio.h>
|
|
|
|
# include <stdlib.h>
|
2002-04-11 15:52:43 +04:00
|
|
|
# include "flstring.h"
|
2016-02-26 19:12:43 +03:00
|
|
|
# include "drivers/X11/Fl_X11_Screen_Driver.H"
|
2016-03-10 20:19:34 +03:00
|
|
|
# include "drivers/X11/Fl_X11_Window_Driver.H"
|
2022-11-07 08:49:40 +03:00
|
|
|
# include "drivers/Unix/Fl_Unix_System_Driver.H"
|
2022-03-15 08:42:06 +03:00
|
|
|
#if FLTK_USE_CAIRO
|
2023-02-14 13:52:21 +03:00
|
|
|
# include "drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.H"
|
2022-03-15 08:42:06 +03:00
|
|
|
#else
|
2016-04-16 12:01:19 +03:00
|
|
|
# include "drivers/Xlib/Fl_Xlib_Graphics_Driver.H"
|
2022-03-15 08:42:06 +03:00
|
|
|
#endif
|
2022-02-03 23:56:42 +03:00
|
|
|
# include "print_button.h"
|
2001-11-27 20:44:08 +03:00
|
|
|
# include <unistd.h>
|
2014-05-23 22:27:55 +04:00
|
|
|
# include <time.h>
|
2001-11-27 20:44:08 +03:00
|
|
|
# include <sys/time.h>
|
2017-06-10 09:10:37 +03:00
|
|
|
# include <math.h>
|
2008-09-11 03:56:49 +04:00
|
|
|
# include <X11/Xmd.h>
|
|
|
|
# include <X11/Xlocale.h>
|
|
|
|
# include <X11/Xlib.h>
|
2011-04-27 12:47:00 +04:00
|
|
|
# include <X11/keysym.h>
|
2014-08-23 12:41:58 +04:00
|
|
|
# include "Xutf8.h"
|
2022-02-03 23:56:42 +03:00
|
|
|
|
2022-03-15 08:42:06 +03:00
|
|
|
#if FLTK_USE_CAIRO
|
|
|
|
# include <cairo-xlib.h>
|
|
|
|
# include <cairo/cairo.h>
|
|
|
|
#endif // FLTK_USE_CAIRO
|
|
|
|
|
2011-10-04 20:56:09 +04:00
|
|
|
#define USE_XRANDR (HAVE_DLSYM && HAVE_DLFCN_H) // means attempt to dynamically load libXrandr.so
|
2011-10-04 13:21:47 +04:00
|
|
|
#if USE_XRANDR
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#define RRScreenChangeNotifyMask (1L << 0) // from X11/extensions/Xrandr.h
|
2020-07-01 19:03:10 +03:00
|
|
|
#define RRScreenChangeNotify 0 // from X11/extensions/Xrandr.h
|
2012-06-14 12:36:43 +04:00
|
|
|
typedef int (*XRRUpdateConfiguration_type)(XEvent *event);
|
|
|
|
static XRRUpdateConfiguration_type XRRUpdateConfiguration_f;
|
2011-10-04 13:21:47 +04:00
|
|
|
static int randrEventBase; // base of RandR-defined events
|
2011-09-30 18:46:08 +04:00
|
|
|
#endif
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2013-09-11 16:54:40 +04:00
|
|
|
# if HAVE_XFIXES
|
|
|
|
# include <X11/extensions/Xfixes.h>
|
|
|
|
static int xfixes_event_base = 0;
|
|
|
|
static bool have_xfixes = false;
|
|
|
|
# endif
|
|
|
|
|
2014-06-16 15:17:57 +04:00
|
|
|
# include <X11/cursorfont.h>
|
|
|
|
|
|
|
|
# if HAVE_XCURSOR
|
|
|
|
# include <X11/Xcursor/Xcursor.h>
|
|
|
|
# endif
|
2015-03-16 14:07:00 +03:00
|
|
|
# if HAVE_XRENDER
|
|
|
|
# include <X11/extensions/Xrender.h>
|
|
|
|
# endif
|
2010-03-14 21:07:24 +03:00
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
|
|
|
# include <poll.h>
|
|
|
|
# else
|
|
|
|
# define POLLIN 1
|
|
|
|
# endif /* USE_POLL */
|
1999-04-17 05:02:30 +04:00
|
|
|
|
2022-03-04 17:40:29 +03:00
|
|
|
extern Fl_Widget *fl_selection_requestor;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2022-03-04 17:40:29 +03:00
|
|
|
static void open_display_i(Display *d); // open display (internal)
|
1998-10-06 22:21:25 +04:00
|
|
|
|
1999-04-17 05:02:30 +04:00
|
|
|
|
2014-09-15 13:17:56 +04:00
|
|
|
extern int fl_send_system_handlers(void *e);
|
|
|
|
|
2021-11-05 14:46:21 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
1998-12-07 16:34:27 +03:00
|
|
|
static Fl_Window* send_motion;
|
1999-05-06 10:20:47 +04:00
|
|
|
extern Fl_Window* fl_xmousewin;
|
2003-03-09 05:00:06 +03:00
|
|
|
#endif
|
2021-11-05 15:12:52 +03:00
|
|
|
|
2003-03-09 05:00:06 +03:00
|
|
|
static bool in_a_window; // true if in any of our windows, even destroyed ones
|
1998-10-06 22:21:25 +04:00
|
|
|
static void do_queued_events() {
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
|
|
|
while (XEventsQueued(fl_display,QueuedAfterReading)) {
|
1998-10-06 22:21:25 +04:00
|
|
|
XEvent xevent;
|
|
|
|
XNextEvent(fl_display, &xevent);
|
2014-09-15 13:17:56 +04:00
|
|
|
if (fl_send_system_handlers(&xevent))
|
|
|
|
continue;
|
1998-10-06 22:21:25 +04:00
|
|
|
fl_handle(xevent);
|
|
|
|
}
|
2002-04-10 05:32:03 +04:00
|
|
|
// we send FL_LEAVE only if the mouse did not enter some other window:
|
2003-03-09 05:00:06 +03:00
|
|
|
if (!in_a_window) Fl::handle(FL_LEAVE, 0);
|
2021-11-05 14:46:21 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
2021-04-26 15:15:55 +03:00
|
|
|
else if (send_motion && send_motion == fl_xmousewin) {
|
1998-12-07 16:34:27 +03:00
|
|
|
send_motion = 0;
|
1999-05-06 10:20:47 +04:00
|
|
|
Fl::handle(FL_MOVE, fl_xmousewin);
|
1998-12-07 16:34:27 +03:00
|
|
|
}
|
2003-03-09 05:00:06 +03:00
|
|
|
#endif
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2001-12-07 01:16:49 +03:00
|
|
|
|
2000-06-18 04:38:41 +04:00
|
|
|
// 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.
|
2022-11-07 08:49:40 +03:00
|
|
|
int Fl_X11_Screen_Driver::poll_or_select_with_delay(double time_to_wait) {
|
1998-10-06 22:21:25 +04:00
|
|
|
// OpenGL and other broken libraries call XEventsQueued
|
|
|
|
// unnecessarily and thus cause the file descriptor to not be ready,
|
|
|
|
// so we must check for already-read events:
|
2000-06-18 04:38:41 +04:00
|
|
|
if (fl_display && XQLength(fl_display)) {do_queued_events(); return 1;}
|
2022-11-07 08:49:40 +03:00
|
|
|
return Fl_Unix_Screen_Driver::poll_or_select_with_delay(time_to_wait);
|
2000-06-18 04:38:41 +04:00
|
|
|
}
|
|
|
|
|
2022-11-07 08:49:40 +03:00
|
|
|
// just like Fl_X11_Screen_Driver::poll_or_select_with_delay(0.0) except no callbacks are done:
|
|
|
|
int Fl_X11_Screen_Driver::poll_or_select() {
|
2000-06-18 04:38:41 +04:00
|
|
|
if (XQLength(fl_display)) return 1;
|
2022-11-07 08:49:40 +03:00
|
|
|
return Fl_Unix_Screen_Driver::poll_or_select();
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2010-12-10 00:52:07 +03:00
|
|
|
// replace \r\n by \n
|
2010-12-10 15:05:01 +03:00
|
|
|
static void convert_crlf(unsigned char *string, long& len) {
|
2010-12-11 17:59:22 +03:00
|
|
|
unsigned char *a, *b;
|
|
|
|
a = b = string;
|
2011-01-16 01:47:30 +03:00
|
|
|
while (*a) {
|
2010-12-11 17:59:22 +03:00
|
|
|
if (*a == '\r' && a[1] == '\n') { a++; len--; }
|
|
|
|
else *b++ = *a++;
|
2010-12-10 15:05:01 +03:00
|
|
|
}
|
2010-12-11 17:59:22 +03:00
|
|
|
*b = 0;
|
2010-12-10 00:52:07 +03:00
|
|
|
}
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Display *fl_display;
|
2022-03-15 08:42:06 +03:00
|
|
|
Display *fl_x11_display() { return fl_display; }
|
2008-09-11 03:56:49 +04:00
|
|
|
Window fl_message_window = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
int fl_screen;
|
|
|
|
XVisualInfo *fl_visual;
|
|
|
|
Colormap fl_colormap;
|
2008-09-11 03:56:49 +04:00
|
|
|
static XRectangle status_area;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2002-03-07 22:22:58 +03:00
|
|
|
static Atom WM_DELETE_WINDOW;
|
|
|
|
static Atom WM_PROTOCOLS;
|
|
|
|
static Atom fl_MOTIF_WM_HINTS;
|
|
|
|
static Atom TARGETS;
|
|
|
|
static Atom CLIPBOARD;
|
2013-09-11 16:54:40 +04:00
|
|
|
static Atom TIMESTAMP;
|
|
|
|
static Atom PRIMARY_TIMESTAMP;
|
|
|
|
static Atom CLIPBOARD_TIMESTAMP;
|
2002-01-10 00:50:02 +03:00
|
|
|
Atom fl_XdndAware;
|
|
|
|
Atom fl_XdndSelection;
|
|
|
|
Atom fl_XdndEnter;
|
|
|
|
Atom fl_XdndTypeList;
|
|
|
|
Atom fl_XdndPosition;
|
|
|
|
Atom fl_XdndLeave;
|
|
|
|
Atom fl_XdndDrop;
|
|
|
|
Atom fl_XdndStatus;
|
|
|
|
Atom fl_XdndActionCopy;
|
|
|
|
Atom fl_XdndFinished;
|
2006-01-04 22:53:34 +03:00
|
|
|
Atom fl_XdndURIList;
|
2014-08-21 16:13:47 +04:00
|
|
|
static Atom fl_Xatextplainutf;
|
2020-07-01 19:03:10 +03:00
|
|
|
static Atom fl_Xatextplainutf2; // STR#2930
|
2014-08-21 16:13:47 +04:00
|
|
|
static Atom fl_Xatextplain;
|
2010-11-30 19:36:38 +03:00
|
|
|
static Atom fl_XaText;
|
2014-08-21 16:13:47 +04:00
|
|
|
static Atom fl_XaCompoundText;
|
2008-09-11 03:56:49 +04:00
|
|
|
Atom fl_XaUtf8String;
|
2014-08-21 16:13:47 +04:00
|
|
|
static Atom fl_XaTextUriList;
|
|
|
|
static Atom fl_XaImageBmp;
|
|
|
|
static Atom fl_XaImagePNG;
|
|
|
|
static Atom fl_INCR;
|
2015-04-20 15:02:10 +03:00
|
|
|
static Atom fl_NET_WM_PID;
|
2020-07-01 19:03:10 +03:00
|
|
|
static Atom fl_NET_WM_NAME; // utf8 aware window label
|
|
|
|
static Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name
|
2014-08-21 16:13:47 +04:00
|
|
|
static Atom fl_NET_SUPPORTING_WM_CHECK;
|
|
|
|
static Atom fl_NET_WM_STATE;
|
|
|
|
static Atom fl_NET_WM_STATE_FULLSCREEN;
|
2023-11-04 13:30:45 +03:00
|
|
|
static Atom fl_NET_WM_STATE_MAXIMIZED_VERT;
|
|
|
|
static Atom fl_NET_WM_STATE_MAXIMIZED_HORZ;
|
2014-08-21 16:13:47 +04:00
|
|
|
static Atom fl_NET_WM_FULLSCREEN_MONITORS;
|
2016-02-10 23:26:51 +03:00
|
|
|
Atom fl_NET_WORKAREA;
|
2014-08-21 16:13:47 +04:00
|
|
|
static Atom fl_NET_WM_ICON;
|
2014-09-05 16:33:35 +04:00
|
|
|
static Atom fl_NET_ACTIVE_WINDOW;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2021-01-29 02:06:34 +03:00
|
|
|
/*
|
|
|
|
Debug: translate Atom (number) to name: enable (1) if used below
|
|
|
|
*/
|
|
|
|
#if (0)
|
|
|
|
static void debug_atom_name(const char *fun, int line, Atom atom) {
|
|
|
|
char *name = XGetAtomName(fl_display, atom);
|
|
|
|
fprintf(stderr, "[%s:%d] debug_atom_name (%4lu) = %s\n", fun, line, atom, name);
|
|
|
|
XFree(name);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
Find the best target in a list of available copy/paste or dnd targets.
|
|
|
|
|
|
|
|
This function searches all available targets [avail, na] by comparing
|
|
|
|
these with an ordered list of suitable targets [targets, nt].
|
|
|
|
The size of the list of available targets is determined by 'na'.
|
|
|
|
|
|
|
|
The list of suitable targets must be ordered by preference with the
|
|
|
|
best target first. This ensures that we always find our preferred
|
|
|
|
target, no matter how the list of available targets is ordered.
|
|
|
|
The list size is max. 'nt' entries but can also be terminated
|
|
|
|
by a zero (0) entry.
|
|
|
|
|
|
|
|
The list of suitable targets is provided by FLTK (see below); the
|
|
|
|
list of available targets is provided by the data source program.
|
|
|
|
|
|
|
|
Returns: (Atom) the "best target" or 0 (zero) if no target matches.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static Atom find_target(Atom *targets, int nt, Atom *avail, int na) {
|
|
|
|
Atom retval = 0;
|
|
|
|
Atom mt, at;
|
|
|
|
int i = 0, m = 0;
|
|
|
|
#if (0) // Debug
|
|
|
|
fprintf(stderr, "find_target: looking for:\n");
|
|
|
|
for (i = 0; i < nt; i++)
|
|
|
|
debug_atom_name(" find_target", i, targets[i]);
|
|
|
|
for (i = 0; i < na; i++)
|
|
|
|
debug_atom_name(" available ", i, avail[i]);
|
|
|
|
#endif // Debug
|
|
|
|
int n = nt;
|
|
|
|
for (i = 0; i < na; i++) { // search all available targets
|
|
|
|
at = avail[i];
|
|
|
|
// search only *better* targets: m < n !
|
|
|
|
for (m = 0; m < n; m++) { // try to match usable targets
|
|
|
|
mt = targets[m];
|
|
|
|
if (!mt) break; // end of list
|
|
|
|
if (mt == at) {
|
|
|
|
n = m;
|
|
|
|
retval = mt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (n == 0) break; // found the *best* target (0)
|
|
|
|
}
|
|
|
|
// debug_atom_name("find_target: FOUND", n, retval);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Find a suitable target for text insertion in a list of available targets.
|
|
|
|
*/
|
|
|
|
static Atom find_target_text(Atom *avail, int na) {
|
|
|
|
static Atom text_targets[] = {
|
|
|
|
fl_XaUtf8String, // "UTF8_STRING"
|
|
|
|
fl_Xatextplainutf2, // "text/plain;charset=utf-8" (correct: lowercase)
|
|
|
|
fl_Xatextplainutf, // "text/plain;charset=UTF-8" (non-standard: compat.)
|
|
|
|
fl_Xatextplain, // "text/plain"
|
|
|
|
XA_STRING, // "STRING"
|
|
|
|
fl_XaText, // "TEXT"
|
|
|
|
fl_XaCompoundText, // "COMPOUND_TEXT"
|
|
|
|
fl_XaTextUriList, // "text/uri-list" (e.g. file manager)
|
|
|
|
(Atom)0 // list terminator (optional, but don't remove)
|
|
|
|
};
|
|
|
|
static int nt = sizeof(text_targets) / sizeof(Atom) - 1; // list size w/o terminator
|
|
|
|
return find_target(text_targets, nt, avail, na);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Find a suitable target for image insertion in a list of available targets.
|
|
|
|
*/
|
|
|
|
static Atom find_target_image(Atom *avail, int na) {
|
|
|
|
static Atom image_targets[] = {
|
|
|
|
fl_XaImageBmp, // "image/bmp"
|
|
|
|
fl_XaImagePNG, // "image/png"
|
|
|
|
(Atom)0 // list terminator (optional, but don't remove)
|
|
|
|
};
|
|
|
|
static int ni = sizeof(image_targets) / sizeof(Atom) - 1; // list size w/o terminator
|
|
|
|
return find_target(image_targets, ni, avail, na);
|
|
|
|
}
|
|
|
|
|
2010-11-15 10:14:29 +03:00
|
|
|
/*
|
|
|
|
X defines 32-bit-entities to have a format value of max. 32,
|
|
|
|
although sizeof(atom) can be 8 (64 bits) on a 64-bit OS.
|
|
|
|
See also fl_open_display() for sizeof(atom) < 4.
|
|
|
|
Used for XChangeProperty (see STR #2419).
|
|
|
|
*/
|
|
|
|
static int atom_bits = 32;
|
|
|
|
|
2000-11-20 22:02:20 +03:00
|
|
|
static void fd_callback(int,void *) {
|
|
|
|
do_queued_events();
|
2000-08-20 08:35:17 +04:00
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2000-11-20 22:02:20 +03:00
|
|
|
extern "C" {
|
|
|
|
static int io_error_handler(Display*) {
|
|
|
|
Fl::fatal("X I/O error");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xerror_handler(Display* d, XErrorEvent* e) {
|
|
|
|
char buf1[128], buf2[128];
|
2022-09-26 17:12:18 +03:00
|
|
|
snprintf(buf1, 128, "XRequest.%d", e->request_code);
|
2000-11-20 22:02:20 +03:00
|
|
|
XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128);
|
|
|
|
XGetErrorText(d, e->error_code, buf1, 128);
|
|
|
|
Fl::warning("%s: %s 0x%lx", buf2, buf1, e->resourceid);
|
|
|
|
return 0;
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2008-09-11 03:56:49 +04:00
|
|
|
extern char *fl_get_font_xfld(int fnum, int size);
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
void Fl_X11_Screen_Driver::new_ic()
|
2008-09-11 03:56:49 +04:00
|
|
|
{
|
2009-03-07 18:15:29 +03:00
|
|
|
XVaNestedList preedit_attr = NULL;
|
|
|
|
XVaNestedList status_attr = NULL;
|
|
|
|
static XFontSet fs = NULL;
|
|
|
|
char *fnt;
|
2014-09-19 20:28:16 +04:00
|
|
|
char **missing_list = 0;
|
|
|
|
int missing_count = 0;
|
2009-03-07 18:15:29 +03:00
|
|
|
char *def_string;
|
|
|
|
static XRectangle spot;
|
|
|
|
int predit = 0;
|
|
|
|
int sarea = 0;
|
|
|
|
XIMStyles* xim_styles = NULL;
|
2008-09-11 03:56:49 +04:00
|
|
|
|
|
|
|
#if USE_XFT
|
2009-03-15 06:14:43 +03:00
|
|
|
|
|
|
|
#if defined(__GNUC__)
|
2010-10-28 22:02:20 +04:00
|
|
|
// FIXME: warning XFT support here
|
2009-03-15 06:14:43 +03:00
|
|
|
#endif /*__GNUC__*/
|
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
if (!fs) {
|
2011-01-14 14:48:18 +03:00
|
|
|
fnt = (char*)"-misc-fixed-*";
|
2009-03-07 18:15:29 +03:00
|
|
|
fs = XCreateFontSet(fl_display, fnt, &missing_list,
|
|
|
|
&missing_count, &def_string);
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
#else
|
2009-03-07 18:15:29 +03:00
|
|
|
if (!fs) {
|
2011-01-14 14:48:18 +03:00
|
|
|
bool must_free_fnt = true;
|
2009-03-07 18:15:29 +03:00
|
|
|
fnt = fl_get_font_xfld(0, 14);
|
2010-10-28 22:02:20 +04:00
|
|
|
if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
|
2009-03-07 18:15:29 +03:00
|
|
|
fs = XCreateFontSet(fl_display, fnt, &missing_list,
|
|
|
|
&missing_count, &def_string);
|
2011-01-14 14:48:18 +03:00
|
|
|
if (must_free_fnt) free(fnt);
|
2009-03-07 18:15:29 +03:00
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
#endif
|
2014-09-19 20:28:16 +04:00
|
|
|
|
|
|
|
if (missing_list) XFreeStringList(missing_list);
|
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
preedit_attr = XVaCreateNestedList(0,
|
|
|
|
XNSpotLocation, &spot,
|
|
|
|
XNFontSet, fs, NULL);
|
|
|
|
status_attr = XVaCreateNestedList(0,
|
|
|
|
XNAreaNeeded, &status_area,
|
|
|
|
XNFontSet, fs, NULL);
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
if (!XGetIMValues(xim_im, XNQueryInputStyle,
|
2009-03-07 18:15:29 +03:00
|
|
|
&xim_styles, NULL, NULL)) {
|
|
|
|
int i;
|
|
|
|
XIMStyle *style;
|
|
|
|
for (i = 0, style = xim_styles->supported_styles;
|
|
|
|
i < xim_styles->count_styles; i++, style++) {
|
|
|
|
if (*style == (XIMPreeditPosition | XIMStatusArea)) {
|
|
|
|
sarea = 1;
|
|
|
|
predit = 1;
|
|
|
|
} else if (*style == (XIMPreeditPosition | XIMStatusNothing)) {
|
|
|
|
predit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XFree(xim_styles);
|
|
|
|
|
|
|
|
if (sarea) {
|
2022-06-19 11:23:24 +03:00
|
|
|
xim_ic = XCreateIC(xim_im,
|
2009-03-07 18:15:29 +03:00
|
|
|
XNInputStyle, (XIMPreeditPosition | XIMStatusArea),
|
|
|
|
XNPreeditAttributes, preedit_attr,
|
|
|
|
XNStatusAttributes, status_attr,
|
|
|
|
NULL);
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
if (!xim_ic && predit) {
|
|
|
|
xim_ic = XCreateIC(xim_im,
|
2009-03-07 18:15:29 +03:00
|
|
|
XNInputStyle, (XIMPreeditPosition | XIMStatusNothing),
|
|
|
|
XNPreeditAttributes, preedit_attr,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
XFree(preedit_attr);
|
|
|
|
XFree(status_attr);
|
2022-06-19 11:23:24 +03:00
|
|
|
if (!xim_ic) {
|
|
|
|
Fl_X11_Screen_Driver::fl_is_over_the_spot = 0;
|
|
|
|
xim_ic = XCreateIC(xim_im,
|
2009-03-07 18:15:29 +03:00
|
|
|
XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
|
|
|
|
NULL);
|
|
|
|
} else {
|
2022-06-19 11:23:24 +03:00
|
|
|
Fl_X11_Screen_Driver::fl_is_over_the_spot = 1;
|
2009-03-07 18:15:29 +03:00
|
|
|
status_attr = XVaCreateNestedList(0, XNAreaNeeded, &status_area, NULL);
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
XGetICValues(xim_ic, XNStatusAttributes, status_attr, NULL);
|
2009-03-07 18:15:29 +03:00
|
|
|
XFree(status_attr);
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
void Fl_X11_Screen_Driver::set_status(int x, int y, int w, int h)
|
2008-09-11 03:56:49 +04:00
|
|
|
{
|
2009-03-07 18:15:29 +03:00
|
|
|
XVaNestedList status_attr;
|
|
|
|
status_area.x = x;
|
|
|
|
status_area.y = y;
|
|
|
|
status_area.width = w;
|
|
|
|
status_area.height = h;
|
2022-06-19 11:23:24 +03:00
|
|
|
if (!xim_ic) return;
|
2009-03-07 18:15:29 +03:00
|
|
|
status_attr = XVaCreateNestedList(0, XNArea, &status_area, NULL);
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
XSetICValues(xim_ic, XNStatusAttributes, status_attr, NULL);
|
2009-03-07 18:15:29 +03:00
|
|
|
XFree(status_attr);
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
|
|
|
|
void Fl_X11_Screen_Driver::init_xim() {
|
2011-02-26 17:14:50 +03:00
|
|
|
static int xim_warning = 2;
|
|
|
|
if (xim_warning > 0) xim_warning--;
|
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
//XIMStyle *style;
|
|
|
|
XIMStyles *xim_styles;
|
|
|
|
if (!fl_display) return;
|
2022-06-19 11:23:24 +03:00
|
|
|
if (xim_im) return;
|
2009-03-07 18:15:29 +03:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
|
2009-03-07 18:15:29 +03:00
|
|
|
xim_styles = NULL;
|
2022-06-19 11:23:24 +03:00
|
|
|
xim_ic = NULL;
|
2009-03-07 18:15:29 +03:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
if (xim_im) {
|
|
|
|
XGetIMValues (xim_im, XNQueryInputStyle,
|
2009-03-07 18:15:29 +03:00
|
|
|
&xim_styles, NULL, NULL);
|
|
|
|
} else {
|
2011-02-26 17:14:50 +03:00
|
|
|
if (xim_warning)
|
|
|
|
Fl::warning("XOpenIM() failed");
|
2011-01-15 01:06:41 +03:00
|
|
|
// if xim_styles is allocated, free it now
|
2011-02-26 17:14:50 +03:00
|
|
|
if (xim_styles) XFree(xim_styles);
|
2009-03-07 18:15:29 +03:00
|
|
|
return;
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
if (xim_styles && xim_styles->count_styles) {
|
2022-06-19 11:23:24 +03:00
|
|
|
Fl_X11_Screen_Driver::new_ic();
|
2009-03-07 18:15:29 +03:00
|
|
|
} else {
|
2011-02-26 17:14:50 +03:00
|
|
|
if (xim_warning)
|
|
|
|
Fl::warning("No XIM style found");
|
2022-06-19 11:23:24 +03:00
|
|
|
XCloseIM(xim_im);
|
|
|
|
xim_im = NULL;
|
2011-01-15 01:06:41 +03:00
|
|
|
// if xim_styles is allocated, free it now
|
2011-02-26 17:14:50 +03:00
|
|
|
if (xim_styles) XFree(xim_styles);
|
2009-03-07 18:15:29 +03:00
|
|
|
return;
|
|
|
|
}
|
2022-06-19 11:23:24 +03:00
|
|
|
if (!xim_ic) {
|
2011-02-26 17:14:50 +03:00
|
|
|
if (xim_warning)
|
|
|
|
Fl::warning("XCreateIC() failed");
|
2022-06-19 11:23:24 +03:00
|
|
|
XCloseIM(xim_im);
|
|
|
|
xim_im = NULL;
|
2009-03-07 18:15:29 +03:00
|
|
|
}
|
2011-01-15 01:06:41 +03:00
|
|
|
// if xim_styles is still allocated, free it now
|
|
|
|
if(xim_styles) XFree(xim_styles);
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
|
2016-04-17 21:07:11 +03:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
void Fl_X11_Screen_Driver::xim_activate(Window xid) {
|
|
|
|
if (!xim_im)
|
2014-09-15 13:44:35 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
// If the focused window has changed, then use the brute force method
|
|
|
|
// of completely recreating the input context.
|
2022-06-19 11:23:24 +03:00
|
|
|
if (xim_win != xid) {
|
|
|
|
xim_deactivate();
|
2014-09-15 13:44:35 +04:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
Fl_X11_Screen_Driver::new_ic();
|
|
|
|
xim_win = xid;
|
2014-09-15 13:44:35 +04:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
XSetICValues(xim_ic,
|
|
|
|
XNFocusWindow, xim_win,
|
|
|
|
XNClientWindow, xim_win,
|
2014-09-15 13:44:35 +04:00
|
|
|
NULL);
|
|
|
|
}
|
2022-07-01 17:54:42 +03:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
Fl_X11_Screen_Driver *driver = (Fl_X11_Screen_Driver*)Fl::screen_driver();
|
|
|
|
driver->set_spot(fl_spotf, fl_spots, fl_spot.x, fl_spot.y, fl_spot.width, fl_spot.height, NULL);
|
2014-09-15 13:44:35 +04:00
|
|
|
}
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
void Fl_X11_Screen_Driver::xim_deactivate(void) {
|
|
|
|
if (!xim_ic)
|
2014-09-15 13:44:35 +04:00
|
|
|
return;
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
XDestroyIC(xim_ic);
|
|
|
|
xim_ic = NULL;
|
2014-09-15 13:44:35 +04:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
xim_win = 0;
|
2014-09-15 13:44:35 +04:00
|
|
|
}
|
|
|
|
|
2016-04-15 18:12:22 +03:00
|
|
|
void Fl_X11_Screen_Driver::enable_im() {
|
2014-09-15 13:44:35 +04:00
|
|
|
Fl_Window *win;
|
|
|
|
|
|
|
|
win = Fl::first_window();
|
|
|
|
if (win && win->shown()) {
|
2022-06-19 11:23:24 +03:00
|
|
|
xim_activate(fl_xid(win));
|
|
|
|
XSetICFocus(xim_ic);
|
2014-09-15 13:44:35 +04:00
|
|
|
} else {
|
2022-06-19 11:23:24 +03:00
|
|
|
new_ic();
|
2014-09-15 13:44:35 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 18:12:22 +03:00
|
|
|
void Fl_X11_Screen_Driver::disable_im() {
|
2022-06-19 11:23:24 +03:00
|
|
|
xim_deactivate();
|
2014-09-15 13:44:35 +04:00
|
|
|
}
|
|
|
|
|
2024-04-04 13:01:04 +03:00
|
|
|
static void delayed_create_print_window(void *) {
|
|
|
|
Fl::remove_check(delayed_create_print_window);
|
|
|
|
fl_create_print_window();
|
|
|
|
}
|
|
|
|
|
2017-06-01 17:05:47 +03:00
|
|
|
void Fl_X11_Screen_Driver::open_display_platform() {
|
2023-12-21 16:22:08 +03:00
|
|
|
static Display *d = NULL;
|
|
|
|
if (d) return;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2008-09-11 03:56:49 +04:00
|
|
|
setlocale(LC_CTYPE, "");
|
2020-02-10 15:38:26 +03:00
|
|
|
XSetLocaleModifiers("");
|
2008-09-11 03:56:49 +04:00
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
XSetIOErrorHandler(io_error_handler);
|
|
|
|
XSetErrorHandler(xerror_handler);
|
|
|
|
|
2023-12-21 16:22:08 +03:00
|
|
|
d = (fl_display ? fl_display : XOpenDisplay(0));
|
2020-06-24 21:20:11 +03:00
|
|
|
if (!d) {
|
|
|
|
Fl::fatal("Can't open display: %s", XDisplayName(0)); // does not return
|
|
|
|
return; // silence static code analyzer
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2020-02-03 17:58:13 +03:00
|
|
|
open_display_i(d);
|
2016-03-01 23:44:56 +03:00
|
|
|
// the unique GC used by all X windows
|
2023-12-21 16:22:08 +03:00
|
|
|
GC gc = XCreateGC(fl_display, RootWindow(fl_display, fl_screen), 0, 0);
|
2016-11-09 12:49:48 +03:00
|
|
|
Fl_Graphics_Driver::default_driver().gc(gc);
|
2024-04-04 13:01:04 +03:00
|
|
|
Fl::add_check(delayed_create_print_window);
|
2000-02-23 12:23:53 +03:00
|
|
|
}
|
|
|
|
|
2011-09-30 18:46:08 +04:00
|
|
|
|
2020-02-03 17:58:13 +03:00
|
|
|
void open_display_i(Display* d) {
|
1998-10-06 22:21:25 +04:00
|
|
|
fl_display = d;
|
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
WM_DELETE_WINDOW = XInternAtom(d, "WM_DELETE_WINDOW", 0);
|
|
|
|
WM_PROTOCOLS = XInternAtom(d, "WM_PROTOCOLS", 0);
|
|
|
|
fl_MOTIF_WM_HINTS = XInternAtom(d, "_MOTIF_WM_HINTS", 0);
|
|
|
|
TARGETS = XInternAtom(d, "TARGETS", 0);
|
|
|
|
CLIPBOARD = XInternAtom(d, "CLIPBOARD", 0);
|
2013-09-11 16:54:40 +04:00
|
|
|
TIMESTAMP = XInternAtom(d, "TIMESTAMP", 0);
|
|
|
|
PRIMARY_TIMESTAMP = XInternAtom(d, "PRIMARY_TIMESTAMP", 0);
|
|
|
|
CLIPBOARD_TIMESTAMP = XInternAtom(d, "CLIPBOARD_TIMESTAMP", 0);
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_XdndAware = XInternAtom(d, "XdndAware", 0);
|
|
|
|
fl_XdndSelection = XInternAtom(d, "XdndSelection", 0);
|
|
|
|
fl_XdndEnter = XInternAtom(d, "XdndEnter", 0);
|
|
|
|
fl_XdndTypeList = XInternAtom(d, "XdndTypeList", 0);
|
|
|
|
fl_XdndPosition = XInternAtom(d, "XdndPosition", 0);
|
|
|
|
fl_XdndLeave = XInternAtom(d, "XdndLeave", 0);
|
|
|
|
fl_XdndDrop = XInternAtom(d, "XdndDrop", 0);
|
|
|
|
fl_XdndStatus = XInternAtom(d, "XdndStatus", 0);
|
|
|
|
fl_XdndActionCopy = XInternAtom(d, "XdndActionCopy", 0);
|
|
|
|
fl_XdndFinished = XInternAtom(d, "XdndFinished", 0);
|
|
|
|
fl_XdndURIList = XInternAtom(d, "text/uri-list", 0);
|
2010-11-30 19:36:38 +03:00
|
|
|
fl_Xatextplainutf = XInternAtom(d, "text/plain;charset=UTF-8",0);
|
2020-07-01 19:03:10 +03:00
|
|
|
fl_Xatextplainutf2 = XInternAtom(d, "text/plain;charset=utf-8",0); // Firefox/Thunderbird needs this - See STR#2930
|
2010-11-30 19:36:38 +03:00
|
|
|
fl_Xatextplain = XInternAtom(d, "text/plain", 0);
|
2011-01-16 01:47:30 +03:00
|
|
|
fl_XaText = XInternAtom(d, "TEXT", 0);
|
2010-11-30 19:36:38 +03:00
|
|
|
fl_XaCompoundText = XInternAtom(d, "COMPOUND_TEXT", 0);
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_XaUtf8String = XInternAtom(d, "UTF8_STRING", 0);
|
|
|
|
fl_XaTextUriList = XInternAtom(d, "text/uri-list", 0);
|
2014-05-23 20:47:21 +04:00
|
|
|
fl_XaImageBmp = XInternAtom(d, "image/bmp", 0);
|
|
|
|
fl_XaImagePNG = XInternAtom(d, "image/png", 0);
|
|
|
|
fl_INCR = XInternAtom(d, "INCR", 0);
|
2015-04-20 15:02:10 +03:00
|
|
|
fl_NET_WM_PID = XInternAtom(d, "_NET_WM_PID", 0);
|
2010-12-01 11:13:27 +03:00
|
|
|
fl_NET_WM_NAME = XInternAtom(d, "_NET_WM_NAME", 0);
|
|
|
|
fl_NET_WM_ICON_NAME = XInternAtom(d, "_NET_WM_ICON_NAME", 0);
|
2012-03-23 20:47:53 +04:00
|
|
|
fl_NET_SUPPORTING_WM_CHECK = XInternAtom(d, "_NET_SUPPORTING_WM_CHECK", 0);
|
|
|
|
fl_NET_WM_STATE = XInternAtom(d, "_NET_WM_STATE", 0);
|
|
|
|
fl_NET_WM_STATE_FULLSCREEN = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", 0);
|
2023-11-04 13:30:45 +03:00
|
|
|
fl_NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(d, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
|
|
|
|
fl_NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(d, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
|
2014-06-11 13:10:53 +04:00
|
|
|
fl_NET_WM_FULLSCREEN_MONITORS = XInternAtom(d, "_NET_WM_FULLSCREEN_MONITORS", 0);
|
2012-06-14 12:36:43 +04:00
|
|
|
fl_NET_WORKAREA = XInternAtom(d, "_NET_WORKAREA", 0);
|
2014-06-16 15:39:32 +04:00
|
|
|
fl_NET_WM_ICON = XInternAtom(d, "_NET_WM_ICON", 0);
|
2014-09-05 16:33:35 +04:00
|
|
|
fl_NET_ACTIVE_WINDOW = XInternAtom(d, "_NET_ACTIVE_WINDOW", 0);
|
2011-01-16 01:47:30 +03:00
|
|
|
|
2010-11-15 10:14:29 +03:00
|
|
|
if (sizeof(Atom) < 4)
|
|
|
|
atom_bits = sizeof(Atom) * 8;
|
2002-01-10 00:50:02 +03:00
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl::add_fd(ConnectionNumber(d), POLLIN, fd_callback);
|
|
|
|
|
2002-03-07 22:22:58 +03:00
|
|
|
fl_screen = DefaultScreen(d);
|
|
|
|
|
|
|
|
fl_message_window =
|
|
|
|
XCreateSimpleWindow(d, RootWindow(d,fl_screen), 0,0,1,1,0, 0, 0);
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
// construct an XVisualInfo that matches the default Visual:
|
|
|
|
XVisualInfo templt; int num;
|
2002-03-07 22:22:58 +03:00
|
|
|
templt.visualid = XVisualIDFromVisual(DefaultVisual(d, fl_screen));
|
|
|
|
fl_visual = XGetVisualInfo(d, VisualIDMask, &templt, &num);
|
|
|
|
fl_colormap = DefaultColormap(d, fl_screen);
|
2022-06-19 11:23:24 +03:00
|
|
|
Fl_X11_Screen_Driver::init_xim();
|
2000-01-23 04:56:42 +03:00
|
|
|
|
2002-03-07 22:22:58 +03:00
|
|
|
#if !USE_COLORMAP
|
2000-01-23 04:56:42 +03:00
|
|
|
Fl::visual(FL_RGB);
|
2002-03-07 22:22:58 +03:00
|
|
|
#endif
|
2013-09-11 16:54:40 +04:00
|
|
|
|
|
|
|
#if HAVE_XFIXES
|
|
|
|
int error_base;
|
|
|
|
if (XFixesQueryExtension(fl_display, &xfixes_event_base, &error_base))
|
|
|
|
have_xfixes = true;
|
|
|
|
else
|
|
|
|
have_xfixes = false;
|
|
|
|
#endif
|
|
|
|
|
2011-10-04 13:21:47 +04:00
|
|
|
#if USE_XRANDR
|
2021-03-31 10:51:10 +03:00
|
|
|
void *libxrandr_addr = Fl_Posix_System_Driver::dlopen_or_dlsym("libXrandr");
|
2011-10-04 13:21:47 +04:00
|
|
|
if (libxrandr_addr) {
|
|
|
|
int error_base;
|
|
|
|
typedef Bool (*XRRQueryExtension_type)(Display*, int*, int*);
|
|
|
|
typedef void (*XRRSelectInput_type)(Display*, Window, int);
|
|
|
|
XRRQueryExtension_type XRRQueryExtension_f = (XRRQueryExtension_type)dlsym(libxrandr_addr, "XRRQueryExtension");
|
|
|
|
XRRSelectInput_type XRRSelectInput_f = (XRRSelectInput_type)dlsym(libxrandr_addr, "XRRSelectInput");
|
2012-06-14 12:36:43 +04:00
|
|
|
XRRUpdateConfiguration_f = (XRRUpdateConfiguration_type)dlsym(libxrandr_addr, "XRRUpdateConfiguration");
|
2011-10-04 13:21:47 +04:00
|
|
|
if (XRRQueryExtension_f && XRRSelectInput_f && XRRQueryExtension_f(d, &randrEventBase, &error_base))
|
|
|
|
XRRSelectInput_f(d, RootWindow(d, fl_screen), RRScreenChangeNotifyMask);
|
2012-06-14 12:36:43 +04:00
|
|
|
else XRRUpdateConfiguration_f = NULL;
|
2011-10-04 13:21:47 +04:00
|
|
|
}
|
2011-09-30 18:46:08 +04:00
|
|
|
#endif
|
2012-06-14 12:36:43 +04:00
|
|
|
|
|
|
|
// Listen for changes to _NET_WORKAREA
|
|
|
|
XSelectInput(d, RootWindow(d, fl_screen), PropertyChangeMask);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2016-04-15 18:36:10 +03:00
|
|
|
void Fl_X11_Screen_Driver::close_display() {
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl::remove_fd(ConnectionNumber(fl_display));
|
|
|
|
XCloseDisplay(fl_display);
|
|
|
|
}
|
|
|
|
|
2017-05-17 14:54:18 +03:00
|
|
|
int Fl_X11_Screen_Driver::get_mouse_unscaled(int &mx, int &my) {
|
2016-04-15 18:36:10 +03:00
|
|
|
open_display();
|
1998-10-06 22:21:25 +04:00
|
|
|
Window root = RootWindow(fl_display, fl_screen);
|
2017-05-17 14:54:18 +03:00
|
|
|
Window c; int cx,cy; unsigned int mask;
|
|
|
|
XQueryPointer(fl_display, root, &root, &c, &mx, &my, &cx, &cy, &mask);
|
|
|
|
#if USE_XFT
|
2017-07-30 19:21:57 +03:00
|
|
|
int screen = screen_num_unscaled(mx, my);
|
|
|
|
return screen >= 0 ? screen : 0;
|
2017-05-17 14:54:18 +03:00
|
|
|
#else
|
|
|
|
return screen_num(mx, my);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-17 09:53:44 +03:00
|
|
|
int Fl_X11_Screen_Driver::get_mouse(int &xx, int &yy) {
|
|
|
|
int snum = get_mouse_unscaled(xx, yy);
|
|
|
|
float s = scale(snum);
|
2017-05-17 14:54:18 +03:00
|
|
|
xx = xx/s;
|
|
|
|
yy = yy/s;
|
2017-06-17 09:53:44 +03:00
|
|
|
return snum;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2002-03-07 22:22:58 +03:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Code used for paste and DnD into the program:
|
|
|
|
|
2002-03-26 20:37:42 +03:00
|
|
|
char *fl_selection_buffer[2];
|
|
|
|
int fl_selection_length[2];
|
2014-05-23 20:47:21 +04:00
|
|
|
const char * fl_selection_type[2];
|
2002-03-26 20:37:42 +03:00
|
|
|
int fl_selection_buffer_length[2];
|
2008-09-11 03:56:49 +04:00
|
|
|
char fl_i_own_selection[2] = {0,0};
|
2002-03-07 22:22:58 +03:00
|
|
|
|
2019-07-18 18:46:10 +03:00
|
|
|
|
2002-03-07 22:22:58 +03:00
|
|
|
// Call this when a "paste" operation happens:
|
2022-02-03 09:03:26 +03:00
|
|
|
void Fl_X11_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) {
|
2002-03-07 22:22:58 +03:00
|
|
|
if (fl_i_own_selection[clipboard]) {
|
|
|
|
// We already have it, do it quickly without window server.
|
2019-07-18 18:46:10 +03:00
|
|
|
if (type == Fl::clipboard_plain_text && fl_selection_type[clipboard] == type) {
|
|
|
|
// Notice that the text is clobbered if set_selection is
|
|
|
|
// called in response to FL_PASTE!
|
|
|
|
// However, for now, we only paste text in this function
|
|
|
|
Fl::e_text = fl_selection_buffer[clipboard];
|
|
|
|
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) {
|
2022-03-04 17:40:29 +03:00
|
|
|
Fl::e_clipboard_data = Fl_Unix_System_Driver::own_bmp_to_RGB(fl_selection_buffer[1]);
|
2019-07-18 18:46:10 +03:00
|
|
|
Fl::e_clipboard_type = Fl::clipboard_image;
|
|
|
|
} else return;
|
|
|
|
int retval = receiver.handle(FL_PASTE);
|
|
|
|
if (retval == 0 && type == Fl::clipboard_image) {
|
|
|
|
delete (Fl_RGB_Image*)Fl::e_clipboard_data;
|
|
|
|
Fl::e_clipboard_data = NULL;
|
|
|
|
}
|
2002-03-07 22:22:58 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// otherwise get the window server to return it:
|
|
|
|
fl_selection_requestor = &receiver;
|
|
|
|
Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
|
2014-05-23 20:47:21 +04:00
|
|
|
Fl::e_clipboard_type = type;
|
2010-11-30 19:36:38 +03:00
|
|
|
XConvertSelection(fl_display, property, TARGETS, property,
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_xid(Fl::first_window()), fl_event_time);
|
2002-03-07 22:22:58 +03:00
|
|
|
}
|
|
|
|
|
2022-07-01 17:54:42 +03:00
|
|
|
int Fl_X11_Screen_Driver::clipboard_contains(const char *type) {
|
2019-07-18 18:46:10 +03:00
|
|
|
if (fl_i_own_selection[1]) {
|
|
|
|
return fl_selection_type[1] == type;
|
|
|
|
}
|
2014-05-23 20:47:21 +04:00
|
|
|
XEvent event;
|
|
|
|
Atom actual; int format; unsigned long count, remaining, i = 0;
|
|
|
|
unsigned char* portion = NULL;
|
|
|
|
Fl_Window *win = Fl::first_window();
|
|
|
|
if (!win || !fl_xid(win)) return 0;
|
2019-07-18 18:46:10 +03:00
|
|
|
win->wait_for_expose();
|
2014-05-29 19:28:04 +04:00
|
|
|
XConvertSelection(fl_display, CLIPBOARD, TARGETS, CLIPBOARD, fl_xid(win), CurrentTime);
|
2014-05-23 20:47:21 +04:00
|
|
|
XFlush(fl_display);
|
2022-07-01 17:54:42 +03:00
|
|
|
// FIXME: The following loop may ignore up to 20 events! (AlbrechtS)
|
|
|
|
do {
|
2020-07-01 19:03:10 +03:00
|
|
|
XNextEvent(fl_display, &event);
|
2014-05-29 19:28:04 +04:00
|
|
|
if (event.type == SelectionNotify && event.xselection.property == None) return 0;
|
2020-07-01 19:03:10 +03:00
|
|
|
i++;
|
2022-07-01 17:54:42 +03:00
|
|
|
} while (i < 20 && event.type != SelectionNotify);
|
2016-05-04 08:59:18 +03:00
|
|
|
if (i >= 20) return 0;
|
2014-05-23 20:47:21 +04:00
|
|
|
XGetWindowProperty(fl_display,
|
2020-07-01 19:03:10 +03:00
|
|
|
event.xselection.requestor,
|
|
|
|
event.xselection.property,
|
|
|
|
0, 4000, 0, 0,
|
|
|
|
&actual, &format, &count, &remaining, &portion);
|
2014-05-23 20:47:21 +04:00
|
|
|
if (actual != XA_ATOM) return 0;
|
2021-01-29 02:06:34 +03:00
|
|
|
Atom t = (Atom)0;
|
|
|
|
if (strcmp(type, Fl::clipboard_plain_text) == 0)
|
|
|
|
t = find_target_text((Atom*)portion, count);
|
|
|
|
else if (strcmp(type, Fl::clipboard_image) == 0)
|
|
|
|
t = find_target_image((Atom*)portion, count);
|
2014-05-23 20:47:21 +04:00
|
|
|
XFree(portion);
|
2021-01-29 02:06:34 +03:00
|
|
|
return (t ? 1 : 0);
|
2014-05-23 20:47:21 +04:00
|
|
|
}
|
|
|
|
|
2014-08-21 16:13:47 +04:00
|
|
|
static Window fl_dnd_source_window;
|
|
|
|
static Atom *fl_dnd_source_types; // null-terminated list of data types being supplied
|
|
|
|
static Atom fl_dnd_type;
|
|
|
|
static Atom fl_dnd_source_action;
|
|
|
|
static Atom fl_dnd_action;
|
2002-03-07 22:22:58 +03:00
|
|
|
|
|
|
|
void fl_sendClientMessage(Window window, Atom message,
|
|
|
|
unsigned long d0,
|
|
|
|
unsigned long d1=0,
|
|
|
|
unsigned long d2=0,
|
|
|
|
unsigned long d3=0,
|
|
|
|
unsigned long d4=0)
|
|
|
|
{
|
|
|
|
XEvent e;
|
|
|
|
e.xany.type = ClientMessage;
|
|
|
|
e.xany.window = window;
|
|
|
|
e.xclient.message_type = message;
|
|
|
|
e.xclient.format = 32;
|
|
|
|
e.xclient.data.l[0] = (long)d0;
|
|
|
|
e.xclient.data.l[1] = (long)d1;
|
|
|
|
e.xclient.data.l[2] = (long)d2;
|
|
|
|
e.xclient.data.l[3] = (long)d3;
|
|
|
|
e.xclient.data.l[4] = (long)d4;
|
|
|
|
XSendEvent(fl_display, window, 0, 0, &e);
|
|
|
|
}
|
|
|
|
|
2012-03-23 20:47:53 +04:00
|
|
|
|
2013-10-09 15:46:36 +04:00
|
|
|
/*
|
|
|
|
Get window property value (32 bit format)
|
2012-03-23 20:47:53 +04:00
|
|
|
Returns zero on success, -1 on error
|
2013-09-20 07:36:02 +04:00
|
|
|
|
|
|
|
'data' should be freed with XFree() using this pattern:
|
2013-10-09 15:46:36 +04:00
|
|
|
|
2013-09-20 07:36:02 +04:00
|
|
|
unsigned long *data = 0;
|
|
|
|
if (0 == get_xwinprop(....., &nitems, &data) ) { ..success.. }
|
|
|
|
else { ..fail.. }
|
|
|
|
if ( data ) { XFree(data); data=0; }
|
2020-07-01 19:03:10 +03:00
|
|
|
|
2013-10-09 15:46:36 +04:00
|
|
|
Note: 'data' can be non-zero, even if the return value is -1 (error) and
|
|
|
|
should hence be XFree'd *after* the if/else statement, as described above.
|
2012-03-23 20:47:53 +04:00
|
|
|
*/
|
|
|
|
static int get_xwinprop(Window wnd, Atom prop, long max_length,
|
|
|
|
unsigned long *nitems, unsigned long **data) {
|
|
|
|
Atom actual;
|
|
|
|
int format;
|
|
|
|
unsigned long bytes_after;
|
2020-07-01 19:03:10 +03:00
|
|
|
|
|
|
|
if (Success != XGetWindowProperty(fl_display, wnd, prop, 0, max_length,
|
|
|
|
False, AnyPropertyType, &actual, &format,
|
2012-03-23 20:47:53 +04:00
|
|
|
nitems, &bytes_after, (unsigned char**)data)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actual == None || format != 32) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-03-07 22:22:58 +03:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Code for copying to clipboard and DnD out of the program:
|
|
|
|
|
2023-01-03 21:40:37 +03:00
|
|
|
// See Fl::copy() for possible values of the destination (argument clipboard)
|
|
|
|
// See also Fl::selection_to_clipboard()
|
2022-02-03 09:03:26 +03:00
|
|
|
void Fl_X11_Screen_Driver::copy(const char *stuff, int len, int clipboard, const char *type) {
|
2002-03-07 22:22:58 +03:00
|
|
|
if (!stuff || len<0) return;
|
2015-05-18 12:10:06 +03:00
|
|
|
|
2023-01-03 21:40:37 +03:00
|
|
|
// if selection_to_clipboard is enabled *and* destination is 0 (selection buffer),
|
|
|
|
// then copy to both (STR 3229)
|
|
|
|
if (clipboard == 0 && Fl::selection_to_clipboard())
|
|
|
|
clipboard = 2;
|
|
|
|
|
2015-05-18 12:10:06 +03:00
|
|
|
if (clipboard >= 2) {
|
2023-01-03 21:40:37 +03:00
|
|
|
copy(stuff, len, 1, type); // copy to clipboard first (this is a recursion!)
|
|
|
|
clipboard = 0; // ... and then to selection buffer: fall through
|
2015-05-18 12:10:06 +03:00
|
|
|
}
|
|
|
|
|
2002-03-26 20:37:42 +03:00
|
|
|
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;
|
2002-03-07 22:22:58 +03:00
|
|
|
}
|
2002-03-26 20:37:42 +03:00
|
|
|
memcpy(fl_selection_buffer[clipboard], stuff, len);
|
|
|
|
fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
|
|
|
|
fl_selection_length[clipboard] = len;
|
2002-03-07 22:22:58 +03:00
|
|
|
fl_i_own_selection[clipboard] = 1;
|
2014-05-23 20:47:21 +04:00
|
|
|
fl_selection_type[clipboard] = Fl::clipboard_plain_text;
|
|
|
|
Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
|
|
|
|
XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-31 09:47:15 +03:00
|
|
|
// takes a raw RGB image and puts it in the copy/paste buffer
|
2016-04-16 15:05:55 +03:00
|
|
|
void Fl_X11_Screen_Driver::copy_image(const unsigned char *data, int W, int H, int clipboard){
|
2016-03-31 09:47:15 +03:00
|
|
|
if (!data || W <= 0 || H <= 0) return;
|
2014-05-23 20:47:21 +04:00
|
|
|
delete[] fl_selection_buffer[clipboard];
|
2022-03-04 17:40:29 +03:00
|
|
|
fl_selection_buffer[clipboard] = (char *) Fl_Unix_System_Driver::create_bmp(data,W,H,&fl_selection_length[clipboard]);
|
2014-05-23 20:47:21 +04:00
|
|
|
fl_selection_buffer_length[clipboard] = fl_selection_length[clipboard];
|
|
|
|
fl_i_own_selection[clipboard] = 1;
|
|
|
|
fl_selection_type[clipboard] = Fl::clipboard_image;
|
2002-03-07 22:22:58 +03:00
|
|
|
Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
|
|
|
|
XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
|
|
|
|
}
|
|
|
|
|
2013-09-11 16:54:40 +04:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Code for tracking clipboard changes:
|
|
|
|
|
2013-09-16 16:02:40 +04:00
|
|
|
static Time primary_timestamp = (Time)-1;
|
|
|
|
static Time clipboard_timestamp = (Time)-1;
|
2013-09-11 16:54:40 +04:00
|
|
|
|
|
|
|
extern bool fl_clipboard_notify_empty(void);
|
|
|
|
extern void fl_trigger_clipboard_notify(int source);
|
|
|
|
|
|
|
|
static void poll_clipboard_owner(void) {
|
|
|
|
Window xid;
|
|
|
|
|
|
|
|
#if HAVE_XFIXES
|
|
|
|
// No polling needed with Xfixes
|
|
|
|
if (have_xfixes)
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// No one is interested, so no point polling
|
|
|
|
if (fl_clipboard_notify_empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We need a window for this to work
|
|
|
|
if (!Fl::first_window())
|
|
|
|
return;
|
|
|
|
xid = fl_xid(Fl::first_window());
|
|
|
|
if (!xid)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Request an update of the selection time for both the primary and
|
|
|
|
// clipboard selections. Magic continues when we get a SelectionNotify.
|
|
|
|
if (!fl_i_own_selection[0])
|
|
|
|
XConvertSelection(fl_display, XA_PRIMARY, TIMESTAMP, PRIMARY_TIMESTAMP,
|
|
|
|
xid, fl_event_time);
|
|
|
|
if (!fl_i_own_selection[1])
|
|
|
|
XConvertSelection(fl_display, CLIPBOARD, TIMESTAMP, CLIPBOARD_TIMESTAMP,
|
|
|
|
xid, fl_event_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clipboard_timeout(void *data)
|
|
|
|
{
|
|
|
|
// No one is interested, so stop polling
|
|
|
|
if (fl_clipboard_notify_empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
poll_clipboard_owner();
|
|
|
|
|
|
|
|
Fl::repeat_timeout(0.5, clipboard_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_clipboard_timestamp(int clipboard, Time time)
|
|
|
|
{
|
|
|
|
Time *timestamp;
|
|
|
|
|
|
|
|
timestamp = clipboard ? &clipboard_timestamp : &primary_timestamp;
|
|
|
|
|
|
|
|
#if HAVE_XFIXES
|
|
|
|
if (!have_xfixes)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
// Initial scan, just store the value
|
|
|
|
if (*timestamp == (Time)-1) {
|
|
|
|
*timestamp = time;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same selection
|
|
|
|
if (time == *timestamp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
*timestamp = time;
|
|
|
|
|
|
|
|
// The clipboard change is the event that caused us to request
|
|
|
|
// the clipboard data, so use that time as the latest event.
|
|
|
|
if (time > fl_event_time)
|
|
|
|
fl_event_time = time;
|
|
|
|
|
|
|
|
// Something happened! Let's tell someone!
|
|
|
|
fl_trigger_clipboard_notify(clipboard);
|
|
|
|
}
|
|
|
|
|
2022-02-03 09:03:26 +03:00
|
|
|
void Fl_X11_Screen_Driver::clipboard_notify_change() {
|
2013-09-11 16:54:40 +04:00
|
|
|
// Reset the timestamps if we've going idle so that you don't
|
|
|
|
// get a bogus immediate trigger next time they're activated.
|
|
|
|
if (fl_clipboard_notify_empty()) {
|
2013-09-16 16:02:40 +04:00
|
|
|
primary_timestamp = (Time)-1;
|
|
|
|
clipboard_timestamp = (Time)-1;
|
2013-09-11 16:54:40 +04:00
|
|
|
} else {
|
|
|
|
#if HAVE_XFIXES
|
|
|
|
if (!have_xfixes)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
poll_clipboard_owner();
|
|
|
|
|
|
|
|
if (!Fl::has_timeout(clipboard_timeout))
|
|
|
|
Fl::add_timeout(0.5, clipboard_timeout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
const XEvent* fl_xevent; // the current x event
|
|
|
|
ulong fl_event_time; // the last timestamp from an x event
|
|
|
|
|
|
|
|
char fl_key_vector[32]; // used by Fl::get_key()
|
|
|
|
|
|
|
|
// Record event mouse position and state from an XEvent:
|
|
|
|
|
|
|
|
static int px, py;
|
|
|
|
static ulong ptime;
|
|
|
|
|
2024-07-07 21:25:12 +03:00
|
|
|
// Citation from XButtonEvent and XKeyEvent docs:
|
|
|
|
// "The state member is set to indicate the logical state of the pointer buttons
|
|
|
|
// and modifier keys just prior to the event, which is the bitwise inclusive OR
|
|
|
|
// of one or more of the button or modifier key masks:
|
|
|
|
// Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask,
|
|
|
|
// ShiftMask, LockMask, ControlMask,
|
|
|
|
// Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask."
|
|
|
|
//
|
|
|
|
// Actual values in Debian Bookworm as of July 2024 (pseudo code):
|
|
|
|
// static int states[] = {
|
|
|
|
// ShiftMask, LockMask, ControlMask, // 1<<0 .. 1<<2
|
|
|
|
// Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, // 1<<3 .. 1<<7
|
|
|
|
// Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask // 1<<8 .. 1<<12
|
|
|
|
// };
|
|
|
|
//
|
2024-09-26 18:59:52 +03:00
|
|
|
// Note: some more (undefined?) state bits *may* be set if the user uses a keyboard
|
2024-07-07 21:25:12 +03:00
|
|
|
// other than the primary one (the top-most in keyboard settings). Therefore we must
|
2024-09-26 18:59:52 +03:00
|
|
|
// take care not to use these undefined bits (found by accident).
|
|
|
|
// These undefined bits are ignored and not set in Fl::event_state(), otherwise we
|
|
|
|
// might overwrite other valid bits (since FLTK 1.4.0, Sep 2024 or later).
|
|
|
|
// See definition of FL_BUTTONS in FL/Enumerations.H:
|
|
|
|
// there are only five "sticky" mouse buttons as of Sep 27, 2024.
|
|
|
|
|
|
|
|
static unsigned int xbutton_state = 0; // extended button state (back, forward)
|
|
|
|
|
|
|
|
// Define the state bits we're interested in for Fl::event_state().
|
|
|
|
// Note that we ignore Button4Mask and Button5Mask (vertical scroll wheel).
|
|
|
|
// X11 doesn't define masks for Button6 and Button7 (horizontal scroll wheel)
|
|
|
|
// and any higher button numbers.
|
|
|
|
|
|
|
|
static const unsigned int event_state_mask =
|
|
|
|
ShiftMask | LockMask | ControlMask |
|
|
|
|
Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask |
|
|
|
|
Button1Mask | Button2Mask | Button3Mask;
|
2024-07-07 21:25:12 +03:00
|
|
|
|
2017-05-17 14:54:18 +03:00
|
|
|
static void set_event_xy(Fl_Window *win) {
|
2021-11-05 14:46:21 +03:00
|
|
|
# if FLTK_CONSOLIDATE_MOTION
|
1998-12-07 16:34:27 +03:00
|
|
|
send_motion = 0;
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
2017-05-17 14:54:18 +03:00
|
|
|
float s = 1;
|
|
|
|
#if USE_XFT
|
2018-05-12 12:36:36 +03:00
|
|
|
s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(win)->screen_num());
|
2017-05-17 14:54:18 +03:00
|
|
|
#endif
|
|
|
|
Fl::e_x_root = fl_xevent->xbutton.x_root/s;
|
|
|
|
Fl::e_x = fl_xevent->xbutton.x/s;
|
|
|
|
Fl::e_y_root = fl_xevent->xbutton.y_root/s;
|
|
|
|
Fl::e_y = fl_xevent->xbutton.y/s;
|
2024-09-26 18:59:52 +03:00
|
|
|
Fl::e_state = ((fl_xevent->xbutton.state & event_state_mask) << 16) | xbutton_state;
|
1998-10-06 22:21:25 +04:00
|
|
|
fl_event_time = fl_xevent->xbutton.time;
|
2001-11-27 20:44:08 +03:00
|
|
|
# ifdef __sgi
|
1998-10-06 22:21:25 +04:00
|
|
|
// get the meta key off PC keyboards:
|
|
|
|
if (fl_key_vector[18]&0x18) Fl::e_state |= FL_META;
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
// turn off is_click if enough time or mouse movement has passed:
|
2000-11-20 05:49:40 +03:00
|
|
|
if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 ||
|
|
|
|
fl_event_time >= ptime+1000)
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl::e_is_click = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this is same event as last && is_click, increment click count:
|
|
|
|
static inline void checkdouble() {
|
|
|
|
if (Fl::e_is_click == Fl::e_keysym)
|
|
|
|
Fl::e_clicks++;
|
|
|
|
else {
|
|
|
|
Fl::e_clicks = 0;
|
|
|
|
Fl::e_is_click = Fl::e_keysym;
|
|
|
|
}
|
|
|
|
px = Fl::e_x_root;
|
|
|
|
py = Fl::e_y_root;
|
|
|
|
ptime = fl_event_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fl_Window* resize_bug_fix;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
2005-11-02 11:26:44 +03:00
|
|
|
static char unknown[] = "<unknown>";
|
|
|
|
const int unknown_len = 10;
|
|
|
|
|
2010-11-17 14:28:58 +03:00
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
static int xerror = 0;
|
|
|
|
|
|
|
|
static int ignoreXEvents(Display *display, XErrorEvent *event) {
|
|
|
|
xerror = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static XErrorHandler catchXExceptions() {
|
|
|
|
xerror = 0;
|
|
|
|
return ignoreXEvents;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wasXExceptionRaised() {
|
|
|
|
return xerror;
|
|
|
|
}
|
|
|
|
|
2022-07-01 17:54:42 +03:00
|
|
|
} // extern "C"
|
2010-11-17 14:28:58 +03:00
|
|
|
|
2022-07-01 17:54:42 +03:00
|
|
|
static bool getNextEvent(XEvent *event_return) {
|
2014-05-23 20:47:21 +04:00
|
|
|
time_t t = time(NULL);
|
2022-07-01 17:54:42 +03:00
|
|
|
while (!XPending(fl_display)) {
|
|
|
|
if (time(NULL) - t > 10.0) {
|
2017-10-15 13:37:29 +03:00
|
|
|
// fprintf(stderr,"Error: The XNextEvent never came...\n");
|
2020-07-01 19:03:10 +03:00
|
|
|
return false;
|
2014-05-23 20:47:21 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
XNextEvent(fl_display, event_return);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-01 17:54:42 +03:00
|
|
|
static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lower_bound) {
|
2017-10-15 13:37:29 +03:00
|
|
|
// fprintf(stderr,"Incremental transfer starting due to INCR property\n");
|
2022-07-02 16:44:46 +03:00
|
|
|
// fprintf(stderr, "[getIncrData:%d] lower_bound [in ] =%10ld\n", __LINE__, lower_bound);
|
|
|
|
const size_t alloc_min = 4 * 1024 * 1024; // min. initial allocation
|
|
|
|
const size_t alloc_max = 200 * 1024 * 1024; // max. initial allocation
|
|
|
|
const size_t alloc_inc = 4 * 1024 * 1024; // (min.) increase if necessary
|
2014-05-23 20:47:21 +04:00
|
|
|
size_t total = 0;
|
2022-07-02 16:44:46 +03:00
|
|
|
size_t data_size = lower_bound + 1;
|
|
|
|
if (data_size < alloc_min) {
|
|
|
|
data_size = alloc_min;
|
|
|
|
} else if (data_size > alloc_max) {
|
|
|
|
data_size = alloc_max;
|
|
|
|
}
|
|
|
|
// fprintf(stderr, "[getIncrData:%d] initial alloc. =%10ld\n", __LINE__, data_size);
|
|
|
|
|
2014-05-23 20:47:21 +04:00
|
|
|
XEvent event;
|
2020-07-01 19:03:10 +03:00
|
|
|
XDeleteProperty(fl_display, selevent.requestor, selevent.property);
|
2022-07-02 16:44:46 +03:00
|
|
|
data = (uchar*)realloc(data, data_size);
|
2022-07-01 18:38:17 +03:00
|
|
|
if (!data) {
|
2022-07-02 16:44:46 +03:00
|
|
|
// fprintf(stderr, "[getIncrData:%d] realloc() FAILED, size = %ld\n", __LINE__, data_size);
|
|
|
|
Fl::fatal("Clipboard data transfer failed, size %ld is too large.", data_size);
|
2022-07-01 18:38:17 +03:00
|
|
|
}
|
2022-07-01 17:54:42 +03:00
|
|
|
for (;;) {
|
2022-07-01 18:38:17 +03:00
|
|
|
if (!getNextEvent(&event)) {
|
|
|
|
// This is unexpected but may happen if the sender (clipboard owner) no longer sends data
|
|
|
|
// fprintf(stderr, "[getIncrData:%d] Failed to get next event (timeout) *** break! ***\n", __LINE__);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (event.type == PropertyNotify) {
|
|
|
|
if (event.xproperty.state != PropertyNewValue) continue; // ignore PropertyDelete
|
2014-05-23 20:47:21 +04:00
|
|
|
Atom actual_type;
|
|
|
|
int actual_format;
|
|
|
|
unsigned long nitems;
|
|
|
|
unsigned long bytes_after;
|
|
|
|
unsigned char* prop = 0;
|
|
|
|
long offset = 0;
|
2022-07-02 16:44:46 +03:00
|
|
|
size_t num_bytes = 0;
|
2017-10-15 13:37:29 +03:00
|
|
|
// size_t slice_size = 0;
|
2022-07-01 17:54:42 +03:00
|
|
|
do {
|
2020-07-01 19:03:10 +03:00
|
|
|
XGetWindowProperty(fl_display, selevent.requestor, selevent.property, offset, 70000, True,
|
|
|
|
AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
|
|
|
|
num_bytes = nitems * (actual_format / 8);
|
|
|
|
offset += num_bytes/4;
|
|
|
|
// slice_size += num_bytes;
|
2022-07-02 16:44:46 +03:00
|
|
|
if (total + num_bytes + bytes_after + 1 > data_size) {
|
|
|
|
data_size += alloc_inc;
|
|
|
|
if (total + num_bytes + bytes_after + 1 > data_size)
|
|
|
|
data_size = total + num_bytes + bytes_after + 1;
|
|
|
|
// printf(" -- realloc(%9ld), total=%10ld, num_bytes=%7ld, bytes_after=%7ld (%7ld), required=%10ld\n",
|
|
|
|
// data_size, total, num_bytes, bytes_after, num_bytes + bytes_after, total + num_bytes + bytes_after + 1);
|
|
|
|
data = (uchar*)realloc(data, data_size);
|
2022-07-01 18:38:17 +03:00
|
|
|
if (!data) {
|
2022-07-02 16:44:46 +03:00
|
|
|
// fprintf(stderr, "[getIncrData():%d] realloc() FAILED, size = %ld\n", __LINE__, data_size);
|
|
|
|
Fl::fatal("Clipboard data transfer failed, size %ld is too large.", data_size);
|
2022-07-01 18:38:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
memcpy(data + total, prop, num_bytes);
|
|
|
|
total += num_bytes;
|
2020-07-01 19:03:10 +03:00
|
|
|
if (prop) XFree(prop);
|
2014-05-23 20:47:21 +04:00
|
|
|
} while (bytes_after != 0);
|
2017-10-15 13:37:29 +03:00
|
|
|
// fprintf(stderr,"INCR data size:%ld\n", slice_size);
|
2014-05-23 20:47:21 +04:00
|
|
|
if (num_bytes == 0) break;
|
|
|
|
}
|
2022-07-01 18:38:17 +03:00
|
|
|
else {
|
|
|
|
// Unexpected next event. At this point we're handling the INCR protocol and can't deal with
|
|
|
|
// *some* other events due to potential recursions. We *could* call fl_handle(event) to handle
|
|
|
|
// *selected* other events but for the time being we ignore all other events!
|
|
|
|
// Handling the INCR protocol for very large data may take some time and multiple events.
|
|
|
|
// Interleaving "other" events are possible, for instance the KeyRelease event of the
|
|
|
|
// ctrl/v key pressed to insert the clipboard. This solution is not perfect but it can
|
|
|
|
// handle the INCR protocol with very large selections in most cases, although with potential
|
|
|
|
// side effects because other events may be ignored.
|
|
|
|
// See GitHub Issue #451: "Segfault if using very large selections".
|
|
|
|
// Note: the "fix" for Issue #451 is basically to use 'continue' rather than 'break'
|
|
|
|
// Debug:
|
|
|
|
// fprintf(stderr,
|
|
|
|
// "[getIncrData:%d] getNextEvent() returned %d, not PropertyNotify (%d). Event ignored.\n",
|
|
|
|
// __LINE__, event.type, PropertyNotify);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2014-05-23 20:47:21 +04:00
|
|
|
}
|
|
|
|
XDeleteProperty(fl_display, selevent.requestor, selevent.property);
|
2022-07-02 16:44:46 +03:00
|
|
|
// fprintf(stderr, "[getIncrData:%d] total data [out] =%10ld\n", __LINE__, (long)total);
|
2014-05-23 20:47:21 +04:00
|
|
|
return (long)total;
|
|
|
|
}
|
|
|
|
|
2020-01-31 17:06:21 +03:00
|
|
|
/*
|
|
|
|
Internal function to reduce "deprecated" warnings for XKeycodeToKeysym().
|
|
|
|
This way we get at most one warning. The option to use XkbKeycodeToKeysym()
|
|
|
|
instead would not help much - see STR #2913 for more information.
|
|
|
|
|
|
|
|
Update (Jan 31, 2020): disable "deprecated declaration" warnings in
|
|
|
|
this function for GCC >= 4.6 and clang (all versions) to get rid of
|
|
|
|
these warnings at least for current GCC and clang compilers.
|
|
|
|
|
|
|
|
Note: '#pragma GCC diagnostic push' needs at least GCC 4.6.
|
2014-10-05 20:51:24 +04:00
|
|
|
*/
|
2020-01-31 17:06:21 +03:00
|
|
|
|
2020-02-09 13:39:50 +03:00
|
|
|
#ifdef __clang__
|
2020-01-31 17:06:21 +03:00
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
2020-02-09 13:39:50 +03:00
|
|
|
#endif
|
2020-01-31 17:06:21 +03:00
|
|
|
|
|
|
|
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
#endif
|
|
|
|
|
2014-10-05 20:51:24 +04:00
|
|
|
static KeySym fl_KeycodeToKeysym(Display *d, KeyCode k, unsigned i) {
|
|
|
|
return XKeycodeToKeysym(d, k, i);
|
|
|
|
}
|
2010-11-17 14:28:58 +03:00
|
|
|
|
2020-01-31 17:06:21 +03:00
|
|
|
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
#endif
|
|
|
|
|
2020-02-09 13:39:50 +03:00
|
|
|
#ifdef __clang__
|
2020-01-31 17:54:51 +03:00
|
|
|
#pragma clang diagnostic pop
|
2020-02-09 13:39:50 +03:00
|
|
|
#endif
|
2020-01-31 17:06:21 +03:00
|
|
|
|
2018-06-08 14:31:30 +03:00
|
|
|
#if USE_XRANDR
|
|
|
|
static void react_to_screen_reconfiguration() {
|
|
|
|
#if USE_XFT
|
|
|
|
// memorize previous screen sizes and scales
|
|
|
|
int old_count = Fl::screen_count();
|
|
|
|
int (*sizes)[4] = new int[old_count][4];
|
|
|
|
float *scales = new float[old_count];
|
|
|
|
for (int screen = 0; screen < old_count; screen++) {
|
|
|
|
Fl::screen_xywh(sizes[screen][0], sizes[screen][1], sizes[screen][2], sizes[screen][3], screen);
|
|
|
|
scales[screen] = Fl::screen_scale(screen);
|
|
|
|
}
|
|
|
|
#endif // USE_XFT
|
|
|
|
Fl::call_screen_init(); // compute new screen sizes
|
|
|
|
#if USE_XFT
|
|
|
|
// detect whether screen sizes were unchanged
|
|
|
|
bool nochange = (old_count == Fl::screen_count());
|
|
|
|
if (nochange) {
|
|
|
|
for (int screen = 0; screen < old_count; screen++) {
|
|
|
|
int X,Y,W,H;
|
|
|
|
Fl::screen_xywh(X,Y,W,H, screen);
|
|
|
|
X /= scales[screen];
|
|
|
|
Y /= scales[screen];
|
|
|
|
W /= scales[screen];
|
|
|
|
H /= scales[screen];
|
|
|
|
if (X != sizes[screen][0] || Y != sizes[screen][1] || W != sizes[screen][2] || H != sizes[screen][3]) {
|
|
|
|
nochange = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete[] sizes;
|
2019-03-11 18:38:35 +03:00
|
|
|
if (nochange || (old_count == 1 && Fl::screen_count() == 1)) {
|
|
|
|
// screen sizes did not change or single screen: re-use previous screen scale values
|
2018-06-08 14:31:30 +03:00
|
|
|
for (int screen = 0; screen < old_count; screen++)
|
|
|
|
Fl::screen_driver()->scale(screen, scales[screen]);
|
|
|
|
} else {
|
2019-03-06 13:10:37 +03:00
|
|
|
Fl::screen_driver()->use_startup_scale_factor();
|
2019-03-06 16:59:47 +03:00
|
|
|
float new_scale = Fl::screen_driver()->scale(0);
|
|
|
|
for (int screen = 0; screen < Fl::screen_count(); screen++) {
|
|
|
|
Fl::screen_driver()->scale(screen, 1);
|
2024-03-04 18:25:45 +03:00
|
|
|
Fl::screen_driver()->rescale_all_windows_from_screen(screen, new_scale, 1);
|
2019-03-06 16:59:47 +03:00
|
|
|
}
|
2018-06-08 14:31:30 +03:00
|
|
|
}
|
|
|
|
delete[] scales;
|
|
|
|
#endif // USE_XFT
|
|
|
|
}
|
|
|
|
#endif // USE_XRANDR
|
|
|
|
|
2019-03-11 18:20:29 +03:00
|
|
|
#if USE_XFT
|
|
|
|
static void after_display_rescale(float *p_current_xft_dpi) {
|
2024-03-12 17:40:45 +03:00
|
|
|
Display *new_dpy = XOpenDisplay(XDisplayString(fl_display));
|
|
|
|
if (!new_dpy) return;
|
|
|
|
char *s = XGetDefault(new_dpy, "Xft", "dpi");
|
|
|
|
float dpi;
|
|
|
|
if (s && sscanf(s, "%f", &dpi) == 1) {
|
|
|
|
//printf("%s previous=%g dpi=%g \n", s, *p_current_xft_dpi, dpi);
|
|
|
|
if (fabs(dpi - *p_current_xft_dpi) > 0.1) {
|
|
|
|
*p_current_xft_dpi = dpi;
|
|
|
|
float f = dpi / 96.;
|
|
|
|
for (int i = 0; i < Fl::screen_count(); i++)
|
|
|
|
Fl::screen_driver()->rescale_all_windows_from_screen(i, f, f);
|
2019-03-11 18:20:29 +03:00
|
|
|
}
|
|
|
|
}
|
2024-03-12 17:40:45 +03:00
|
|
|
XCloseDisplay(new_dpy);
|
2019-03-11 18:20:29 +03:00
|
|
|
}
|
|
|
|
#endif // USE_XFT
|
2018-06-08 14:31:30 +03:00
|
|
|
|
2024-03-18 16:05:48 +03:00
|
|
|
|
|
|
|
static Window *xid_vector = NULL; // list of FLTK-created xid's (see issue #935)
|
|
|
|
static int xid_vector_size = 0;
|
|
|
|
static int xid_vector_count = 0;
|
|
|
|
|
|
|
|
static void add_xid_vector(Window xid) {
|
|
|
|
if (xid_vector_count >= xid_vector_size) {
|
|
|
|
xid_vector_size += 10;
|
|
|
|
xid_vector = (Window*)realloc(xid_vector, xid_vector_size * sizeof(Window));
|
|
|
|
}
|
|
|
|
xid_vector[xid_vector_count++] = xid;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool remove_xid_vector(Window xid) {
|
|
|
|
for (int pos = xid_vector_count - 1; pos >= 0; pos--) {
|
|
|
|
if (xid_vector[pos] == xid) {
|
|
|
|
if (pos != --xid_vector_count) xid_vector[pos] = xid_vector[xid_vector_count];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2003-01-31 18:50:28 +03:00
|
|
|
int fl_handle(const XEvent& thisevent)
|
1998-10-06 22:21:25 +04:00
|
|
|
{
|
2003-01-31 18:50:28 +03:00
|
|
|
XEvent xevent = thisevent;
|
|
|
|
fl_xevent = &thisevent;
|
1998-12-29 17:07:14 +03:00
|
|
|
Window xid = xevent.xany.window;
|
2024-03-19 17:09:14 +03:00
|
|
|
|
2024-03-18 16:05:48 +03:00
|
|
|
// For each DestroyNotify event, determine whether an FLTK-created window
|
|
|
|
// is being destroyed (see issue #935).
|
|
|
|
bool xid_is_from_fltk_win = false;
|
|
|
|
if (xevent.type == DestroyNotify) {
|
|
|
|
xid_is_from_fltk_win = remove_xid_vector(xid);
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
|
2024-03-18 16:05:48 +03:00
|
|
|
// The following if statement is limited to cases when event DestroyNotify
|
|
|
|
// concerns a non-FLTK window. Thus, the possibly slow call to XOpenIM()
|
|
|
|
// is not performed when an FLTK-created window is closed. This fixes issue #935.
|
2022-06-19 11:23:24 +03:00
|
|
|
if (Fl_X11_Screen_Driver::xim_ic && xevent.type == DestroyNotify &&
|
2024-03-18 16:05:48 +03:00
|
|
|
xid != Fl_X11_Screen_Driver::xim_win && !fl_find(xid) && !xid_is_from_fltk_win)
|
2008-09-11 03:56:49 +04:00
|
|
|
{
|
2024-03-18 16:05:48 +03:00
|
|
|
// When using menus or tooltips: xid is a just hidden top-level FLTK win, xim_win is non-FLTK;
|
|
|
|
// after XIM crash: xid is non-FLTK.
|
|
|
|
// Trigger XIM crash under Debian: kill process containing "ibus-daemon"
|
|
|
|
// Restart XIM after triggered crash: "ibus-daemon --panel disable --xim &"
|
2008-09-11 03:56:49 +04:00
|
|
|
XIM xim_im;
|
|
|
|
xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
|
|
|
|
if (!xim_im) {
|
|
|
|
/* XIM server has crashed */
|
2020-02-10 15:38:26 +03:00
|
|
|
XSetLocaleModifiers("");
|
2022-06-19 11:23:24 +03:00
|
|
|
Fl_X11_Screen_Driver::xim_im = NULL;
|
|
|
|
Fl_X11_Screen_Driver::init_xim();
|
2008-09-11 03:56:49 +04:00
|
|
|
} else {
|
2020-07-01 19:03:10 +03:00
|
|
|
XCloseIM(xim_im); // see STR 2185 for comment
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
if (Fl_X11_Screen_Driver::xim_ic && (xevent.type == FocusIn))
|
|
|
|
Fl_X11_Screen_Driver::xim_activate(xid);
|
2008-09-11 03:56:49 +04:00
|
|
|
|
2022-06-19 11:23:24 +03:00
|
|
|
if (Fl_X11_Screen_Driver::xim_ic && XFilterEvent((XEvent *)&xevent, 0))
|
2010-11-17 00:15:27 +03:00
|
|
|
return(1);
|
2020-07-01 19:03:10 +03:00
|
|
|
|
|
|
|
#if USE_XRANDR
|
2012-06-14 12:36:43 +04:00
|
|
|
if( XRRUpdateConfiguration_f && xevent.type == randrEventBase + RRScreenChangeNotify) {
|
|
|
|
XRRUpdateConfiguration_f(&xevent);
|
2018-06-08 14:31:30 +03:00
|
|
|
react_to_screen_reconfiguration();
|
2011-09-30 18:46:08 +04:00
|
|
|
Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL);
|
|
|
|
}
|
2017-05-17 14:54:18 +03:00
|
|
|
#endif // USE_XRANDR
|
2012-06-14 12:36:43 +04:00
|
|
|
|
|
|
|
if (xevent.type == PropertyNotify && xevent.xproperty.atom == fl_NET_WORKAREA) {
|
2019-03-11 18:20:29 +03:00
|
|
|
Fl_X11_Screen_Driver *d = (Fl_X11_Screen_Driver*)Fl::screen_driver();
|
|
|
|
d->init_workarea();
|
|
|
|
#if USE_XFT
|
|
|
|
after_display_rescale(&(d->current_xft_dpi));
|
|
|
|
#endif // USE_XFT
|
2012-06-14 12:36:43 +04:00
|
|
|
}
|
2020-07-01 19:03:10 +03:00
|
|
|
|
1998-12-29 17:07:14 +03:00
|
|
|
switch (xevent.type) {
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
case KeymapNotify:
|
|
|
|
memcpy(fl_key_vector, xevent.xkeymap.key_vector, 32);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case MappingNotify:
|
|
|
|
XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
|
|
|
|
return 0;
|
1998-12-29 17:07:14 +03:00
|
|
|
|
2002-03-07 22:22:58 +03:00
|
|
|
case SelectionNotify: {
|
2013-09-21 20:41:23 +04:00
|
|
|
static unsigned char* sn_buffer = 0;
|
2022-07-01 18:38:17 +03:00
|
|
|
if (sn_buffer) {
|
|
|
|
free(sn_buffer); sn_buffer = 0;
|
|
|
|
}
|
2002-08-09 07:17:30 +04:00
|
|
|
long bytesread = 0;
|
2002-03-07 22:22:58 +03:00
|
|
|
if (fl_xevent->xselection.property) for (;;) {
|
|
|
|
// The Xdnd code pastes 64K chunks together, possibly to avoid
|
|
|
|
// bugs in X servers, or maybe to avoid an extra round-trip to
|
|
|
|
// get the property length. I copy this here:
|
|
|
|
Atom actual; int format; unsigned long count, remaining;
|
2011-05-30 20:47:48 +04:00
|
|
|
unsigned char* portion = NULL;
|
2002-03-07 22:22:58 +03:00
|
|
|
if (XGetWindowProperty(fl_display,
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_xevent->xselection.requestor,
|
|
|
|
fl_xevent->xselection.property,
|
2014-06-01 21:21:37 +04:00
|
|
|
bytesread/4, 65536, 1, AnyPropertyType,
|
2009-03-07 18:15:29 +03:00
|
|
|
&actual, &format, &count, &remaining,
|
|
|
|
&portion)) break; // quit on error
|
2013-09-11 16:54:40 +04:00
|
|
|
|
|
|
|
if ((fl_xevent->xselection.property == PRIMARY_TIMESTAMP) ||
|
|
|
|
(fl_xevent->xselection.property == CLIPBOARD_TIMESTAMP)) {
|
|
|
|
if (portion && format == 32 && count == 1) {
|
|
|
|
Time t = *(unsigned int*)portion;
|
|
|
|
if (fl_xevent->xselection.property == CLIPBOARD_TIMESTAMP)
|
|
|
|
handle_clipboard_timestamp(1, t);
|
|
|
|
else
|
|
|
|
handle_clipboard_timestamp(0, t);
|
|
|
|
}
|
2022-07-01 17:54:42 +03:00
|
|
|
XFree(portion);
|
2013-09-11 16:54:40 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-11-30 19:36:38 +03:00
|
|
|
if (actual == TARGETS || actual == XA_ATOM) {
|
2021-01-29 02:06:34 +03:00
|
|
|
Atom type;
|
2020-07-01 19:03:10 +03:00
|
|
|
if (Fl::e_clipboard_type == Fl::clipboard_image) { // searching for image data
|
2021-01-29 02:06:34 +03:00
|
|
|
type = find_target_image((Atom *)portion, count);
|
2020-07-01 19:03:10 +03:00
|
|
|
}
|
2021-01-29 02:06:34 +03:00
|
|
|
else { // searching for text data. *FIXME* - there may be other data types!
|
|
|
|
type = find_target_text((Atom *)portion, count);
|
2020-07-01 19:03:10 +03:00
|
|
|
}
|
2022-07-01 17:54:42 +03:00
|
|
|
XFree(portion);
|
2021-01-29 02:06:34 +03:00
|
|
|
|
|
|
|
if (!type) { // not found
|
|
|
|
if (Fl::e_clipboard_type == Fl::clipboard_image)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
type = fl_XaUtf8String; // text: try this anyway (was: XA_STRING)
|
|
|
|
} // not found
|
|
|
|
|
2020-07-01 19:03:10 +03:00
|
|
|
Atom property = xevent.xselection.property;
|
|
|
|
XConvertSelection(fl_display, property, type, property,
|
|
|
|
fl_xid(Fl::first_window()),
|
|
|
|
fl_event_time);
|
|
|
|
if (type == fl_XaImageBmp) {
|
|
|
|
Fl::e_clipboard_type = Fl::clipboard_image;
|
2021-01-29 02:06:34 +03:00
|
|
|
}
|
2020-07-01 19:03:10 +03:00
|
|
|
else if (type == fl_XaImagePNG) {
|
|
|
|
Fl::e_clipboard_type = Fl::clipboard_image;
|
2021-01-29 02:06:34 +03:00
|
|
|
}
|
2020-07-01 19:03:10 +03:00
|
|
|
else {
|
|
|
|
Fl::e_clipboard_type = Fl::clipboard_plain_text;
|
2021-01-29 02:06:34 +03:00
|
|
|
}
|
2020-07-01 19:03:10 +03:00
|
|
|
return true;
|
2002-03-07 22:22:58 +03:00
|
|
|
}
|
2021-01-29 02:06:34 +03:00
|
|
|
if (actual == fl_INCR) {
|
2022-07-02 16:44:46 +03:00
|
|
|
// From ICCCM: "The contents of the INCR property will be an integer, which
|
|
|
|
// represents a lower bound on the number of bytes of data in the selection."
|
|
|
|
//
|
|
|
|
// However, some X clients don't set the integer ("lower bound") in the INCR
|
|
|
|
// property, hence 'count' below is zero and we must not access '*portion'.
|
|
|
|
// Debug:
|
|
|
|
#if (0)
|
|
|
|
fprintf(stderr,
|
|
|
|
"[fl_handle(SelectionNotify/INCR):%d] actual=%ld (INCR), format=%d, count=%ld, remaining=%ld",
|
|
|
|
__LINE__, actual, format, count, remaining);
|
|
|
|
if (portion && count > 0) {
|
|
|
|
fprintf(stderr,
|
|
|
|
", portion=%p (%ld)", portion, *(long*)portion);
|
|
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
#endif
|
|
|
|
size_t lower_bound = 0;
|
|
|
|
if (portion && count > 0) {
|
|
|
|
lower_bound = *(unsigned long *)portion;
|
|
|
|
}
|
2022-07-01 18:38:17 +03:00
|
|
|
bytesread = getIncrData(sn_buffer, xevent.xselection, lower_bound);
|
2021-01-29 02:06:34 +03:00
|
|
|
XFree(portion);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Make sure we got something sane...
|
2011-05-30 20:47:48 +04:00
|
|
|
if ((portion == NULL) || (format != 8) || (count == 0)) {
|
2022-07-01 17:54:42 +03:00
|
|
|
if (portion) XFree(portion);
|
2011-05-30 20:47:48 +04:00
|
|
|
return true;
|
2013-09-20 07:36:02 +04:00
|
|
|
}
|
2013-09-21 20:41:23 +04:00
|
|
|
sn_buffer = (unsigned char*)realloc(sn_buffer, bytesread+count+remaining+1);
|
2022-07-01 17:54:42 +03:00
|
|
|
memcpy(sn_buffer + bytesread, portion, count);
|
|
|
|
XFree(portion);
|
2011-05-30 20:47:48 +04:00
|
|
|
bytesread += count;
|
|
|
|
// Cannot trust data to be null terminated
|
2013-09-21 20:41:23 +04:00
|
|
|
sn_buffer[bytesread] = '\0';
|
2002-03-07 22:22:58 +03:00
|
|
|
if (!remaining) break;
|
|
|
|
}
|
2014-05-23 20:47:21 +04:00
|
|
|
if (sn_buffer && Fl::e_clipboard_type == Fl::clipboard_plain_text) {
|
2013-09-21 20:41:23 +04:00
|
|
|
sn_buffer[bytesread] = 0;
|
|
|
|
convert_crlf(sn_buffer, bytesread);
|
2010-12-10 15:05:01 +03:00
|
|
|
}
|
2016-05-03 17:28:45 +03:00
|
|
|
if (!fl_selection_requestor) return 0;
|
2014-05-23 20:47:21 +04:00
|
|
|
if (Fl::e_clipboard_type == Fl::clipboard_image) {
|
|
|
|
if (bytesread == 0) return 0;
|
|
|
|
static char tmp_fname[21];
|
|
|
|
static Fl_Shared_Image *shared = 0;
|
|
|
|
strcpy(tmp_fname, "/tmp/clipboardXXXXXX");
|
|
|
|
int fd = mkstemp(tmp_fname);
|
|
|
|
if (fd == -1) return 0;
|
|
|
|
uchar *p = sn_buffer; ssize_t towrite = bytesread, written;
|
|
|
|
while (towrite) {
|
2020-07-01 19:03:10 +03:00
|
|
|
written = write(fd, p, towrite);
|
|
|
|
p += written; towrite -= written;
|
|
|
|
}
|
2014-05-23 20:47:21 +04:00
|
|
|
close(fd);
|
|
|
|
free(sn_buffer); sn_buffer = 0;
|
|
|
|
shared = Fl_Shared_Image::get(tmp_fname);
|
2017-10-15 13:18:53 +03:00
|
|
|
fl_unlink(tmp_fname);
|
2014-05-23 20:47:21 +04:00
|
|
|
if (!shared) return 0;
|
2016-05-03 17:28:45 +03:00
|
|
|
uchar *rgb = new uchar[shared->w() * shared->h() * shared->d()];
|
|
|
|
memcpy(rgb, shared->data()[0], shared->w() * shared->h() * shared->d());
|
|
|
|
Fl_RGB_Image *image = new Fl_RGB_Image(rgb, shared->w(), shared->h(), shared->d());
|
2014-05-23 20:47:21 +04:00
|
|
|
shared->release();
|
2016-05-03 17:28:45 +03:00
|
|
|
image->alloc_array = 1;
|
2014-05-23 20:47:21 +04:00
|
|
|
Fl::e_clipboard_data = (void*)image;
|
|
|
|
}
|
2016-05-03 17:28:45 +03:00
|
|
|
else if (Fl::e_clipboard_type == Fl::clipboard_plain_text) {
|
2014-05-23 20:47:21 +04:00
|
|
|
Fl::e_text = sn_buffer ? (char*)sn_buffer : (char *)"";
|
|
|
|
Fl::e_length = bytesread;
|
2021-01-29 02:06:34 +03:00
|
|
|
}
|
2004-12-03 06:14:17 +03:00
|
|
|
int old_event = Fl::e_number;
|
2016-05-03 17:28:45 +03:00
|
|
|
int retval = fl_selection_requestor->handle(Fl::e_number = FL_PASTE);
|
|
|
|
if (!retval && Fl::e_clipboard_type == Fl::clipboard_image) {
|
|
|
|
delete (Fl_RGB_Image*)Fl::e_clipboard_data;
|
2021-01-29 02:06:34 +03:00
|
|
|
Fl::e_clipboard_data = NULL;
|
2016-05-03 17:28:45 +03:00
|
|
|
}
|
2004-12-03 06:14:17 +03:00
|
|
|
Fl::e_number = old_event;
|
2002-03-07 22:22:58 +03:00
|
|
|
// Detect if this paste is due to Xdnd by the property name (I use
|
2024-06-15 12:33:44 +03:00
|
|
|
// XA_SECONDARY for that) and send an XdndFinished message.
|
|
|
|
// This has to be delayed until now rather than sending it immediately
|
|
|
|
// after calling XConvertSelection because we need to send the success
|
|
|
|
// status (retval) and the performed action to the sender - at least
|
|
|
|
// since XDND protocol version 5 (see docs).
|
|
|
|
// [FIXME: is the condition below really correct?]
|
|
|
|
|
|
|
|
if (fl_xevent->xselection.property == XA_SECONDARY && fl_dnd_source_window) {
|
|
|
|
fl_sendClientMessage(fl_dnd_source_window, // send to window
|
|
|
|
fl_XdndFinished, // XdndFinished message
|
|
|
|
fl_xevent->xselection.requestor, // data.l[0] target window
|
|
|
|
retval ? 1 : 0, // data.l[1] Bit 0: 1 = success
|
|
|
|
retval ? fl_dnd_action : None); // data.l[2] action performed
|
2002-03-07 22:22:58 +03:00
|
|
|
fl_dnd_source_window = 0; // don't send a second time
|
|
|
|
}
|
2021-01-29 02:06:34 +03:00
|
|
|
return 1;
|
|
|
|
} // SelectionNotify
|
2002-03-07 22:22:58 +03:00
|
|
|
|
|
|
|
case SelectionClear: {
|
|
|
|
int clipboard = fl_xevent->xselectionclear.selection == CLIPBOARD;
|
|
|
|
fl_i_own_selection[clipboard] = 0;
|
2013-09-11 16:54:40 +04:00
|
|
|
poll_clipboard_owner();
|
2021-01-29 02:06:34 +03:00
|
|
|
return 1;
|
|
|
|
}
|
2002-03-07 22:22:58 +03:00
|
|
|
|
|
|
|
case SelectionRequest: {
|
|
|
|
XSelectionEvent e;
|
|
|
|
e.type = SelectionNotify;
|
|
|
|
e.requestor = fl_xevent->xselectionrequest.requestor;
|
|
|
|
e.selection = fl_xevent->xselectionrequest.selection;
|
|
|
|
int clipboard = e.selection == CLIPBOARD;
|
|
|
|
e.target = fl_xevent->xselectionrequest.target;
|
|
|
|
e.time = fl_xevent->xselectionrequest.time;
|
|
|
|
e.property = fl_xevent->xselectionrequest.property;
|
2014-05-23 20:47:21 +04:00
|
|
|
if (fl_selection_type[clipboard] == Fl::clipboard_plain_text) {
|
|
|
|
if (e.target == TARGETS) {
|
2020-07-01 19:03:10 +03:00
|
|
|
Atom a[3] = {fl_XaUtf8String, XA_STRING, fl_XaText};
|
|
|
|
XChangeProperty(fl_display, e.requestor, e.property,
|
|
|
|
XA_ATOM, atom_bits, 0, (unsigned char*)a, 3);
|
2014-05-23 20:47:21 +04:00
|
|
|
} else {
|
2021-01-29 02:06:34 +03:00
|
|
|
if (fl_selection_length[clipboard]) { // data available
|
2020-07-01 19:03:10 +03:00
|
|
|
if (e.target == fl_XaUtf8String ||
|
|
|
|
e.target == XA_STRING ||
|
|
|
|
e.target == fl_XaCompoundText ||
|
|
|
|
e.target == fl_XaText ||
|
|
|
|
e.target == fl_Xatextplain ||
|
|
|
|
e.target == fl_Xatextplainutf ||
|
|
|
|
e.target == fl_Xatextplainutf2) {
|
|
|
|
// clobber the target type, this seems to make some applications
|
|
|
|
// behave that insist on asking for XA_TEXT instead of UTF8_STRING
|
|
|
|
// Does not change XA_STRING as that breaks xclipboard.
|
|
|
|
if (e.target != XA_STRING) e.target = fl_XaUtf8String;
|
|
|
|
XChangeProperty(fl_display, e.requestor, e.property,
|
|
|
|
e.target, 8, 0,
|
|
|
|
(unsigned char *)fl_selection_buffer[clipboard],
|
|
|
|
fl_selection_length[clipboard]);
|
|
|
|
}
|
2021-01-29 02:06:34 +03:00
|
|
|
} else { // no data available
|
2020-07-01 19:03:10 +03:00
|
|
|
e.property = 0;
|
|
|
|
}
|
2014-05-23 20:47:21 +04:00
|
|
|
}
|
|
|
|
} else { // image in clipboard
|
|
|
|
if (e.target == TARGETS) {
|
2020-07-01 19:03:10 +03:00
|
|
|
Atom a[1] = {fl_XaImageBmp};
|
|
|
|
XChangeProperty(fl_display, e.requestor, e.property,
|
|
|
|
XA_ATOM, atom_bits, 0, (unsigned char*)a, 1);
|
2014-05-23 20:47:21 +04:00
|
|
|
} else {
|
2020-07-01 19:03:10 +03:00
|
|
|
if (e.target == fl_XaImageBmp && fl_selection_length[clipboard]) {
|
|
|
|
XChangeProperty(fl_display, e.requestor, e.property,
|
|
|
|
e.target, 8, 0,
|
|
|
|
(unsigned char *)fl_selection_buffer[clipboard],
|
|
|
|
fl_selection_length[clipboard]);
|
|
|
|
} else {
|
|
|
|
e.property = 0;
|
|
|
|
}
|
2010-11-30 19:36:38 +03:00
|
|
|
}
|
2002-03-07 22:22:58 +03:00
|
|
|
}
|
2021-01-29 02:06:34 +03:00
|
|
|
XSendEvent(fl_display, e.requestor, 0, 0, (XEvent *)&e);
|
2002-03-07 22:22:58 +03:00
|
|
|
return 1;
|
2021-01-29 02:06:34 +03:00
|
|
|
} // SelectionRequest
|
2002-03-07 22:22:58 +03:00
|
|
|
|
1998-12-29 17:07:14 +03:00
|
|
|
// events where interesting window id is in a different place:
|
|
|
|
case CirculateNotify:
|
|
|
|
case CirculateRequest:
|
|
|
|
case ConfigureNotify:
|
|
|
|
case ConfigureRequest:
|
|
|
|
case CreateNotify:
|
|
|
|
case DestroyNotify:
|
|
|
|
case GravityNotify:
|
|
|
|
case MapNotify:
|
|
|
|
case MapRequest:
|
|
|
|
case ReparentNotify:
|
|
|
|
case UnmapNotify:
|
|
|
|
xid = xevent.xmaprequest.window;
|
|
|
|
break;
|
2021-01-29 02:06:34 +03:00
|
|
|
} // switch (xevent.type)
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
int event = 0;
|
1998-12-29 17:07:14 +03:00
|
|
|
Fl_Window* window = fl_find(xid);
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
if (window) switch (xevent.type) {
|
|
|
|
|
2011-05-23 20:49:02 +04:00
|
|
|
case DestroyNotify: { // an X11 window was closed externally from the program
|
|
|
|
Fl::handle(FL_CLOSE, window);
|
2023-01-13 23:16:17 +03:00
|
|
|
Fl_X* X = Fl_X::flx(window);
|
2011-05-23 20:49:02 +04:00
|
|
|
if (X) { // indicates the FLTK window was not closed
|
2020-07-01 19:03:10 +03:00
|
|
|
X->xid = (Window)0; // indicates the X11 window was already destroyed
|
|
|
|
window->hide();
|
|
|
|
int oldx = window->x(), oldy = window->y();
|
|
|
|
window->position(0, 0);
|
|
|
|
window->position(oldx, oldy);
|
|
|
|
window->show(); // recreate the X11 window in support of the FLTK window
|
2021-01-29 02:06:34 +03:00
|
|
|
}
|
2011-05-23 20:49:02 +04:00
|
|
|
return 1;
|
|
|
|
}
|
2002-01-10 00:50:02 +03:00
|
|
|
case ClientMessage: {
|
|
|
|
Atom message = fl_xevent->xclient.message_type;
|
|
|
|
const long* data = fl_xevent->xclient.data.l;
|
2002-03-07 22:22:58 +03:00
|
|
|
if ((Atom)(data[0]) == WM_DELETE_WINDOW) {
|
2002-01-10 00:50:02 +03:00
|
|
|
event = FL_CLOSE;
|
|
|
|
} else if (message == fl_XdndEnter) {
|
2024-05-11 08:43:27 +03:00
|
|
|
/*
|
|
|
|
Excerpt from the XDND protocol at https://www.freedesktop.org/wiki/Specifications/XDND/ :
|
|
|
|
- data.l[0] contains the XID of the source window.
|
|
|
|
- data.l[1]:
|
|
|
|
Bit 0 is set if the source supports more than three data types.
|
2024-06-15 12:33:44 +03:00
|
|
|
The high byte contains the protocol version to use (minimum of the source's and
|
2024-05-11 08:43:27 +03:00
|
|
|
target's highest supported versions). The rest of the bits are reserved for future use.
|
|
|
|
- data.l[2,3,4] contain the first three types that the source supports. Unused slots are set
|
|
|
|
to None. The ordering is arbitrary.
|
2024-06-15 12:33:44 +03:00
|
|
|
|
2024-05-11 08:43:27 +03:00
|
|
|
If the Source supports more than three data types, bit 0 of data.l[1] is set. This tells the
|
|
|
|
Target to check the property XdndTypeList on the Source window for the list of available
|
|
|
|
types. This property should contain all the available types.
|
2024-06-15 12:33:44 +03:00
|
|
|
|
2024-05-11 08:43:27 +03:00
|
|
|
BUT wayland gnome apps (e.g., gnome-text-editor) set bit 0 of data.l[1]
|
|
|
|
even though their source supports 2 data types (UTF8 text + a gnome-specific type)
|
|
|
|
and put None (==0) in each of data.l[2,3,4].
|
|
|
|
The same gnome apps run in X11 mode (GDK_BACKEND=x11) clear bit 0 of data.l[1]
|
|
|
|
and support only UTF8 text announced in data.l[2].
|
|
|
|
FLTK wayland apps set bit 0 of data.l[1] and support only UTF8 text.
|
2024-06-15 12:33:44 +03:00
|
|
|
|
2024-05-11 08:43:27 +03:00
|
|
|
Overall, the correct procedure is
|
|
|
|
if (bit 0 of data.l[1] is set) {
|
|
|
|
get the XdndTypeList property
|
|
|
|
use all the data types it returns which can be in any number ≥ 1
|
|
|
|
} else {
|
|
|
|
the source supports 1, 2 or 3 data types available at data.l[2,3,4]
|
|
|
|
}
|
|
|
|
*/
|
2021-11-05 15:12:52 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
2021-11-05 15:12:52 +03:00
|
|
|
#endif // FLTK_CONSOLIDATE_MOTION
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-01-10 00:50:02 +03:00
|
|
|
fl_dnd_source_window = data[0];
|
|
|
|
// version number is data[1]>>24
|
2021-01-29 02:06:34 +03:00
|
|
|
// fprintf(stderr, "XdndEnter, version %ld\n", data[1] >> 24);
|
2002-01-10 00:50:02 +03:00
|
|
|
if (data[1]&1) {
|
2009-03-07 18:15:29 +03:00
|
|
|
// get list of data types:
|
|
|
|
Atom actual; int format; unsigned long count, remaining;
|
2013-09-21 20:41:23 +04:00
|
|
|
unsigned char *cm_buffer = 0;
|
2009-03-07 18:15:29 +03:00
|
|
|
XGetWindowProperty(fl_display, fl_dnd_source_window, fl_XdndTypeList,
|
|
|
|
0, 0x8000000L, False, XA_ATOM, &actual, &format,
|
2013-09-21 20:41:23 +04:00
|
|
|
&count, &remaining, &cm_buffer);
|
2024-05-10 21:26:53 +03:00
|
|
|
if (actual != XA_ATOM || format != 32 || count <= 0 || !cm_buffer) {
|
2013-09-21 20:41:23 +04:00
|
|
|
if ( cm_buffer ) { XFree(cm_buffer); cm_buffer = 0; }
|
2009-03-07 18:15:29 +03:00
|
|
|
goto FAILED;
|
2020-07-01 19:03:10 +03:00
|
|
|
}
|
2009-03-07 18:15:29 +03:00
|
|
|
delete [] fl_dnd_source_types;
|
|
|
|
fl_dnd_source_types = new Atom[count+1];
|
|
|
|
for (unsigned i = 0; i < count; i++) {
|
2013-09-21 20:41:23 +04:00
|
|
|
fl_dnd_source_types[i] = ((Atom*)cm_buffer)[i];
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_dnd_source_types[count] = 0;
|
2013-09-21 20:41:23 +04:00
|
|
|
XFree(cm_buffer); cm_buffer = 0;
|
2002-01-10 00:50:02 +03:00
|
|
|
} else {
|
|
|
|
FAILED:
|
2009-03-07 18:15:29 +03:00
|
|
|
// less than four data types, or if the above messes up:
|
|
|
|
if (!fl_dnd_source_types) fl_dnd_source_types = new Atom[4];
|
|
|
|
fl_dnd_source_types[0] = data[2];
|
|
|
|
fl_dnd_source_types[1] = data[3];
|
|
|
|
fl_dnd_source_types[2] = data[4];
|
|
|
|
fl_dnd_source_types[3] = 0;
|
2002-01-10 00:50:02 +03:00
|
|
|
}
|
2005-02-01 06:13:01 +03:00
|
|
|
|
2021-01-29 02:06:34 +03:00
|
|
|
// Pick the "best" source (text) type...
|
|
|
|
// *FIXME* what if we don't find a suitable type? (see below: first type?)
|
|
|
|
// *FIXME* count (zero terminated) dnd sources (must be at least 1)
|
|
|
|
int dnd_sources;
|
|
|
|
for (dnd_sources = 0; fl_dnd_source_types[dnd_sources]; dnd_sources++) {
|
|
|
|
// empty
|
2005-02-01 06:13:01 +03:00
|
|
|
}
|
2021-01-29 02:06:34 +03:00
|
|
|
fl_dnd_type = find_target_text(fl_dnd_source_types, dnd_sources);
|
|
|
|
if (!fl_dnd_type) // not found: pick first type
|
|
|
|
fl_dnd_type = fl_dnd_source_types[0];
|
2005-02-01 06:13:01 +03:00
|
|
|
|
2002-01-10 00:50:02 +03:00
|
|
|
event = FL_DND_ENTER;
|
2005-11-02 11:26:44 +03:00
|
|
|
Fl::e_text = unknown;
|
|
|
|
Fl::e_length = unknown_len;
|
2002-01-10 00:50:02 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
} else if (message == fl_XdndPosition) {
|
2021-11-05 15:12:52 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
2021-11-05 15:12:52 +03:00
|
|
|
#endif // FLTK_CONSOLIDATE_MOTION
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-01-10 00:50:02 +03:00
|
|
|
fl_dnd_source_window = data[0];
|
2019-02-09 18:28:26 +03:00
|
|
|
float s = 1;
|
|
|
|
#if USE_XFT
|
|
|
|
if (window) s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(window)->screen_num());
|
|
|
|
#endif
|
|
|
|
Fl::e_x_root = (data[2]>>16)/s;
|
|
|
|
Fl::e_y_root = (data[2]&0xFFFF)/s;
|
2002-01-10 00:50:02 +03:00
|
|
|
if (window) {
|
2009-03-07 18:15:29 +03:00
|
|
|
Fl::e_x = Fl::e_x_root-window->x();
|
|
|
|
Fl::e_y = Fl::e_y_root-window->y();
|
2002-01-10 00:50:02 +03:00
|
|
|
}
|
|
|
|
fl_event_time = data[3];
|
|
|
|
fl_dnd_source_action = data[4];
|
|
|
|
fl_dnd_action = fl_XdndActionCopy;
|
2005-11-02 11:26:44 +03:00
|
|
|
Fl::e_text = unknown;
|
|
|
|
Fl::e_length = unknown_len;
|
2002-01-10 00:50:02 +03:00
|
|
|
int accept = Fl::handle(FL_DND_DRAG, window);
|
|
|
|
fl_sendClientMessage(data[0], fl_XdndStatus,
|
|
|
|
fl_xevent->xclient.window,
|
|
|
|
accept ? 1 : 0,
|
|
|
|
0, // used for xy rectangle to not send position inside
|
|
|
|
0, // used for width+height of rectangle
|
|
|
|
accept ? fl_dnd_action : None);
|
2002-02-19 23:21:10 +03:00
|
|
|
return 1;
|
2002-01-10 00:50:02 +03:00
|
|
|
|
|
|
|
} else if (message == fl_XdndLeave) {
|
|
|
|
fl_dnd_source_window = 0; // don't send a finished message to it
|
|
|
|
event = FL_DND_LEAVE;
|
2005-11-02 11:26:44 +03:00
|
|
|
Fl::e_text = unknown;
|
|
|
|
Fl::e_length = unknown_len;
|
2002-01-10 00:50:02 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
} else if (message == fl_XdndDrop) {
|
2021-11-05 15:12:52 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
2021-11-05 15:12:52 +03:00
|
|
|
#endif // FLTK_CONSOLIDATE_MOTION
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-01-10 00:50:02 +03:00
|
|
|
fl_dnd_source_window = data[0];
|
|
|
|
fl_event_time = data[2];
|
|
|
|
Window to_window = fl_xevent->xclient.window;
|
2005-11-02 11:26:44 +03:00
|
|
|
Fl::e_text = unknown;
|
|
|
|
Fl::e_length = unknown_len;
|
2002-01-10 00:50:02 +03:00
|
|
|
if (Fl::handle(FL_DND_RELEASE, window)) {
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_selection_requestor = Fl::belowmouse();
|
2014-06-01 21:21:37 +04:00
|
|
|
Fl::e_clipboard_type = Fl::clipboard_plain_text;
|
2009-03-07 18:15:29 +03:00
|
|
|
XConvertSelection(fl_display, fl_XdndSelection,
|
|
|
|
fl_dnd_type, XA_SECONDARY,
|
|
|
|
to_window, fl_event_time);
|
2002-01-10 00:50:02 +03:00
|
|
|
} else {
|
2009-03-07 18:15:29 +03:00
|
|
|
// Send the finished message if I refuse the drop.
|
|
|
|
// It is not clear whether I can just send finished always,
|
|
|
|
// or if I have to wait for the SelectionNotify event as the
|
|
|
|
// code is currently doing.
|
|
|
|
fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished, to_window);
|
|
|
|
fl_dnd_source_window = 0;
|
2002-01-10 00:50:02 +03:00
|
|
|
}
|
2002-02-19 23:21:10 +03:00
|
|
|
return 1;
|
2002-01-10 00:50:02 +03:00
|
|
|
|
|
|
|
}
|
2021-01-29 02:06:34 +03:00
|
|
|
break;
|
|
|
|
} // case ClientMessage
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
case UnmapNotify:
|
|
|
|
event = FL_HIDE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Expose:
|
2018-05-12 12:36:36 +03:00
|
|
|
Fl_Window_Driver::driver(window)->wait_for_expose_value = 0;
|
2001-11-27 20:44:08 +03:00
|
|
|
# if 0
|
1998-10-20 01:00:26 +04:00
|
|
|
// try to keep windows on top even if WM_TRANSIENT_FOR does not work:
|
1999-02-03 11:43:35 +03:00
|
|
|
// opaque move/resize window managers do not like this, so I disabled it.
|
1998-10-06 22:21:25 +04:00
|
|
|
if (Fl::first_window()->non_modal() && window != Fl::first_window())
|
|
|
|
Fl::first_window()->show();
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-20 01:00:26 +04:00
|
|
|
|
|
|
|
case GraphicsExpose:
|
2017-05-17 14:54:18 +03:00
|
|
|
{
|
|
|
|
#if USE_XFT
|
2018-05-12 12:36:36 +03:00
|
|
|
int ns = Fl_Window_Driver::driver(window)->screen_num();
|
2017-05-17 14:54:18 +03:00
|
|
|
float s = Fl::screen_driver()->scale(ns);
|
|
|
|
window->damage(FL_DAMAGE_EXPOSE, xevent.xexpose.x/s, xevent.xexpose.y/s,
|
|
|
|
xevent.xexpose.width/s + 2, xevent.xexpose.height/s + 2);
|
|
|
|
#else
|
|
|
|
window->damage(FL_DAMAGE_EXPOSE, xevent.xexpose.x, xevent.xexpose.y,
|
|
|
|
xevent.xexpose.width, xevent.xexpose.height);
|
|
|
|
#endif
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
case FocusIn:
|
2022-06-19 11:23:24 +03:00
|
|
|
if (Fl_X11_Screen_Driver::xim_ic) XSetICFocus(Fl_X11_Screen_Driver::xim_ic);
|
1998-10-06 22:21:25 +04:00
|
|
|
event = FL_FOCUS;
|
2013-09-11 16:54:40 +04:00
|
|
|
// If the user has toggled from another application to this one,
|
|
|
|
// then it's a good time to check for clipboard changes.
|
|
|
|
poll_clipboard_owner();
|
1998-10-06 22:21:25 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FocusOut:
|
2022-06-19 11:23:24 +03:00
|
|
|
if (Fl_X11_Screen_Driver::xim_ic) XUnsetICFocus(Fl_X11_Screen_Driver::xim_ic);
|
1998-10-06 22:21:25 +04:00
|
|
|
event = FL_UNFOCUS;
|
|
|
|
break;
|
|
|
|
|
2001-10-27 07:45:29 +04:00
|
|
|
case KeyPress:
|
|
|
|
case KeyRelease: {
|
2003-01-31 18:50:28 +03:00
|
|
|
KEYPRESS:
|
1999-04-23 10:55:53 +04:00
|
|
|
int keycode = xevent.xkey.keycode;
|
|
|
|
fl_key_vector[keycode/8] |= (1 << (keycode%8));
|
2013-09-21 20:41:23 +04:00
|
|
|
static char *kp_buffer = NULL;
|
|
|
|
static int kp_buffer_len = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
KeySym keysym;
|
2013-09-21 20:41:23 +04:00
|
|
|
if (kp_buffer_len == 0) {
|
|
|
|
kp_buffer_len = 4096;
|
|
|
|
kp_buffer = (char*) malloc(kp_buffer_len);
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
2001-10-27 07:45:29 +04:00
|
|
|
if (xevent.type == KeyPress) {
|
|
|
|
event = FL_KEYDOWN;
|
2008-09-11 03:56:49 +04:00
|
|
|
|
2020-06-24 21:20:11 +03:00
|
|
|
int len;
|
2022-06-19 11:23:24 +03:00
|
|
|
if (Fl_X11_Screen_Driver::xim_ic) {
|
2020-07-01 19:03:10 +03:00
|
|
|
Status status;
|
2022-06-19 11:23:24 +03:00
|
|
|
len = XUtf8LookupString(Fl_X11_Screen_Driver::xim_ic, (XKeyPressedEvent *)&xevent.xkey,
|
2020-07-01 19:03:10 +03:00
|
|
|
kp_buffer, kp_buffer_len, &keysym, &status);
|
|
|
|
|
|
|
|
while (status == XBufferOverflow && kp_buffer_len < 50000) {
|
|
|
|
kp_buffer_len = kp_buffer_len * 5 + 1;
|
|
|
|
kp_buffer = (char*)realloc(kp_buffer, kp_buffer_len);
|
2022-06-19 11:23:24 +03:00
|
|
|
len = XUtf8LookupString(Fl_X11_Screen_Driver::xim_ic, (XKeyPressedEvent *)&xevent.xkey,
|
2020-07-01 19:03:10 +03:00
|
|
|
kp_buffer, kp_buffer_len, &keysym, &status);
|
|
|
|
}
|
|
|
|
keysym = fl_KeycodeToKeysym(fl_display, keycode, 0);
|
2008-09-11 03:56:49 +04:00
|
|
|
} else {
|
2010-11-17 00:15:27 +03:00
|
|
|
//static XComposeStatus compose;
|
|
|
|
len = XLookupString((XKeyEvent*)&(xevent.xkey),
|
2013-09-21 20:41:23 +04:00
|
|
|
kp_buffer, kp_buffer_len, &keysym, 0/*&compose*/);
|
2010-11-17 00:15:27 +03:00
|
|
|
if (keysym && keysym < 0x400) { // a character in latin-1,2,3,4 sets
|
|
|
|
// force it to type a character (not sure if this ever is needed):
|
2013-09-21 20:41:23 +04:00
|
|
|
// if (!len) {kp_buffer[0] = char(keysym); len = 1;}
|
|
|
|
len = fl_utf8encode(XKeysymToUcs(keysym), kp_buffer);
|
2010-11-17 00:15:27 +03:00
|
|
|
if (len < 1) len = 1;
|
|
|
|
// ignore all effects of shift on the keysyms, which makes it a lot
|
2011-04-24 21:09:41 +04:00
|
|
|
// easier to program shortcuts and is Windoze-compatible:
|
2014-10-05 20:51:24 +04:00
|
|
|
keysym = fl_KeycodeToKeysym(fl_display, keycode, 0);
|
2010-11-17 00:15:27 +03:00
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
2013-09-21 20:41:23 +04:00
|
|
|
kp_buffer[len] = 0;
|
|
|
|
Fl::e_text = kp_buffer;
|
2001-10-27 07:45:29 +04:00
|
|
|
Fl::e_length = len;
|
|
|
|
} else {
|
2003-01-31 18:50:28 +03:00
|
|
|
// Stupid X sends fake key-up events when a repeating key is held
|
2008-09-15 21:46:42 +04:00
|
|
|
// down, probably due to some back compatibility problem. Fortunately
|
2003-01-31 18:50:28 +03:00
|
|
|
// we can detect this because the repeating KeyPress event is in
|
|
|
|
// the queue, get it and execute it instead:
|
2011-01-16 01:47:30 +03:00
|
|
|
|
2014-03-13 20:57:52 +04:00
|
|
|
// Bool XkbSetDetectableAutoRepeat ( display, detectable, supported_rtrn )
|
2009-12-13 15:03:26 +03:00
|
|
|
// Display * display ;
|
|
|
|
// Bool detectable ;
|
|
|
|
// Bool * supported_rtrn ;
|
2014-05-23 20:47:21 +04:00
|
|
|
// ...would be the easy way to correct this issue. Unfortunately, this call is also
|
2009-12-13 15:03:26 +03:00
|
|
|
// broken on many Unix distros including Ubuntu and Solaris (as of Dec 2009)
|
|
|
|
|
2011-01-16 01:47:30 +03:00
|
|
|
// Bogus KeyUp events are generated by repeated KeyDown events. One
|
2014-05-23 20:47:21 +04:00
|
|
|
// necessary condition is an identical key event pending right after
|
2009-12-13 15:03:26 +03:00
|
|
|
// the bogus KeyUp.
|
2014-05-23 20:47:21 +04:00
|
|
|
// The new code introduced Dec 2009 differs in that it only checks the very
|
2009-12-13 15:03:26 +03:00
|
|
|
// next event in the queue, not the entire queue of events.
|
|
|
|
// This function wrongly detects a repeat key if a software keyboard
|
2011-01-16 01:47:30 +03:00
|
|
|
// sends a burst of events containing two consecutive equal keys. However,
|
2009-12-13 15:03:26 +03:00
|
|
|
// in every non-gaming situation, this is no problem because both KeyPress
|
|
|
|
// events will cause the expected behavior.
|
|
|
|
XEvent peekevent;
|
|
|
|
if (XPending(fl_display)) {
|
|
|
|
XPeekEvent(fl_display, &peekevent);
|
|
|
|
if ( (peekevent.type == KeyPress) // must be a KeyPress event
|
|
|
|
&& (peekevent.xkey.keycode == xevent.xkey.keycode) // must be the same key
|
|
|
|
&& (peekevent.xkey.time == xevent.xkey.time) // must be sent at the exact same time
|
|
|
|
) {
|
|
|
|
XNextEvent(fl_display, &xevent);
|
|
|
|
goto KEYPRESS;
|
|
|
|
}
|
2003-01-31 18:50:28 +03:00
|
|
|
}
|
2011-01-16 01:47:30 +03:00
|
|
|
|
2001-10-27 07:45:29 +04:00
|
|
|
event = FL_KEYUP;
|
|
|
|
fl_key_vector[keycode/8] &= ~(1 << (keycode%8));
|
|
|
|
// keyup events just get the unshifted keysym:
|
2014-10-05 20:51:24 +04:00
|
|
|
keysym = fl_KeycodeToKeysym(fl_display, keycode, 0);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2001-11-27 20:44:08 +03:00
|
|
|
# ifdef __sgi
|
1999-04-23 10:55:53 +04:00
|
|
|
// You can plug a microsoft keyboard into an sgi but the extra shift
|
|
|
|
// keys are not translated. Make them translate like XFree86 does:
|
|
|
|
if (!keysym) switch(keycode) {
|
1998-10-06 22:21:25 +04:00
|
|
|
case 147: keysym = FL_Meta_L; break;
|
|
|
|
case 148: keysym = FL_Meta_R; break;
|
|
|
|
case 149: keysym = FL_Menu; break;
|
|
|
|
}
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
2016-01-31 07:33:54 +03:00
|
|
|
# ifdef BACKSPACE_HACK
|
1999-04-23 10:55:53 +04:00
|
|
|
// Attempt to fix keyboards that send "delete" for the key in the
|
|
|
|
// upper-right corner of the main keyboard. But it appears that
|
|
|
|
// very few of these remain?
|
2008-09-11 03:56:49 +04:00
|
|
|
static int got_backspace = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
if (!got_backspace) {
|
|
|
|
if (keysym == FL_Delete) keysym = FL_BackSpace;
|
|
|
|
else if (keysym == FL_BackSpace) got_backspace = 1;
|
|
|
|
}
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
2011-04-18 15:45:46 +04:00
|
|
|
// For the first few years, there wasn't a good consensus on what the
|
|
|
|
// Windows keys should be mapped to for X11. So we need to help out a
|
|
|
|
// bit and map all variants to the same FLTK key...
|
|
|
|
switch (keysym) {
|
2020-07-01 19:03:10 +03:00
|
|
|
case XK_Meta_L:
|
|
|
|
case XK_Hyper_L:
|
|
|
|
case XK_Super_L:
|
|
|
|
keysym = FL_Meta_L;
|
|
|
|
break;
|
|
|
|
case XK_Meta_R:
|
|
|
|
case XK_Hyper_R:
|
|
|
|
case XK_Super_R:
|
|
|
|
keysym = FL_Meta_R;
|
|
|
|
break;
|
2011-04-18 15:45:46 +04:00
|
|
|
}
|
2011-05-22 01:55:59 +04:00
|
|
|
// Convert the multimedia keys to safer, portable values
|
|
|
|
switch (keysym) { // XF names come from X11/XF86keysym.h
|
|
|
|
case 0x1008FF11: // XF86XK_AudioLowerVolume:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Volume_Down;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF12: // XF86XK_AudioMute:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Volume_Mute;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF13: // XF86XK_AudioRaiseVolume:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Volume_Up;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF14: // XF86XK_AudioPlay:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Media_Play;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF15: // XF86XK_AudioStop:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Media_Stop;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF16: // XF86XK_AudioPrev:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Media_Prev;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF17: // XF86XK_AudioNext:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Media_Next;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF18: // XF86XK_HomePage:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Home_Page;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF19: // XF86XK_Mail:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Mail;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF1B: // XF86XK_Search:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Search;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF26: // XF86XK_Back:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Back;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF27: // XF86XK_Forward:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Forward;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF28: // XF86XK_Stop:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Stop;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF29: // XF86XK_Refresh:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Refresh;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF2F: // XF86XK_Sleep:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Sleep;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
case 0x1008FF30: // XF86XK_Favorites:
|
2020-07-01 19:03:10 +03:00
|
|
|
keysym = FL_Favorites;
|
|
|
|
break;
|
2011-05-22 01:55:59 +04:00
|
|
|
}
|
2023-08-16 13:35:50 +03:00
|
|
|
|
|
|
|
// Special processing for number keys == keycodes 10-19
|
|
|
|
// necessary to support keyboard layouts with digits in uppercase :
|
|
|
|
if (keycode >= 10 && keycode <= 18) {
|
|
|
|
keysym = '1' + (keycode - 10);
|
|
|
|
} else if (keycode == 19) {
|
|
|
|
keysym = '0';
|
|
|
|
}
|
|
|
|
|
1999-04-23 10:55:53 +04:00
|
|
|
// We have to get rid of the XK_KP_function keys, because they are
|
|
|
|
// not produced on Windoze and thus case statements tend not to check
|
|
|
|
// for them. There are 15 of these in the range 0xff91 ... 0xff9f
|
|
|
|
if (keysym >= 0xff91 && keysym <= 0xff9f) {
|
2004-06-01 05:08:50 +04:00
|
|
|
// Map keypad keysym to character or keysym depending on
|
|
|
|
// numlock state...
|
2014-10-05 20:51:24 +04:00
|
|
|
unsigned long keysym1 = fl_KeycodeToKeysym(fl_display, keycode, 1);
|
2006-06-09 12:06:14 +04:00
|
|
|
if (keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))
|
|
|
|
Fl::e_original_keysym = (int)(keysym1 | FL_KP);
|
2004-06-01 05:08:50 +04:00
|
|
|
if ((xevent.xkey.state & Mod2Mask) &&
|
|
|
|
(keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))) {
|
2009-03-07 18:15:29 +03:00
|
|
|
// Store ASCII numeric keypad value...
|
|
|
|
keysym = keysym1 | FL_KP;
|
2013-09-21 20:41:23 +04:00
|
|
|
kp_buffer[0] = char(keysym1) & 0x7F;
|
2020-06-24 21:20:11 +03:00
|
|
|
// len = 1;
|
1999-04-23 10:55:53 +04:00
|
|
|
} else {
|
2009-03-07 18:15:29 +03:00
|
|
|
// Map keypad to special key...
|
|
|
|
static const unsigned short table[15] = {
|
|
|
|
FL_F+1, FL_F+2, FL_F+3, FL_F+4,
|
|
|
|
FL_Home, FL_Left, FL_Up, FL_Right,
|
|
|
|
FL_Down, FL_Page_Up, FL_Page_Down, FL_End,
|
|
|
|
0xff0b/*XK_Clear*/, FL_Insert, FL_Delete};
|
|
|
|
keysym = table[keysym-0xff91];
|
1999-04-23 10:55:53 +04:00
|
|
|
}
|
2006-06-09 12:06:14 +04:00
|
|
|
} else {
|
|
|
|
// Store this so we can later know if the KP was used
|
|
|
|
Fl::e_original_keysym = (int)keysym;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
Fl::e_keysym = int(keysym);
|
2011-01-16 01:47:30 +03:00
|
|
|
|
2010-10-30 23:56:20 +04:00
|
|
|
// replace XK_ISO_Left_Tab (Shift-TAB) with FL_Tab (modifier flags are set correctly by X11)
|
|
|
|
if (Fl::e_keysym == 0xfe20) Fl::e_keysym = FL_Tab;
|
2011-01-16 01:47:30 +03:00
|
|
|
|
2017-05-17 14:54:18 +03:00
|
|
|
set_event_xy(window);
|
2012-04-24 06:44:21 +04:00
|
|
|
Fl::e_is_click = 0; }
|
|
|
|
break;
|
2020-07-01 19:03:10 +03:00
|
|
|
|
2024-09-26 18:59:52 +03:00
|
|
|
// Mouse button "press" event:
|
|
|
|
// ---------------------------
|
|
|
|
// X11 uses special conventions for mouse "button" numbers:
|
|
|
|
// 1-3: standard mouse buttons left, middle, right in this order
|
|
|
|
// 4-5: scroll wheel up, down - not reflected in Fl::event_state()
|
|
|
|
// 6-7: scroll wheel left, right - not reflected in Fl::event_state()
|
|
|
|
// 8-9: side buttons back, forward - mapped to 4-5, see below
|
|
|
|
// Since X11 pseudo button numbers 4-7 are useless for Fl::event_state() we map
|
|
|
|
// real button numbers 8 and 9 to 4 and 5, respectively in FLTK's button numbers
|
|
|
|
// and in the event state (Fl::event_state()).
|
|
|
|
// Variable `xbutton_state` is used internally to store the status of the extra
|
|
|
|
// mouse buttons 4 (back) and 5 (forward) since X11 doesn't keep their status.
|
|
|
|
|
|
|
|
case ButtonPress: {
|
2024-11-02 19:11:32 +03:00
|
|
|
int mb = xevent.xbutton.button; // mouse button
|
|
|
|
if (mb < 1 || mb > 9) return 0; // unknown or unsupported button, ignore
|
2024-09-26 18:59:52 +03:00
|
|
|
|
|
|
|
// FIXME(?): here we set some event related variables although we *might*
|
|
|
|
// ignore an event sent by X because we don't know or want it. This may lead to
|
|
|
|
// inconsistencies in Fl::event_key(), Fl::event_state() and more (see set_event_xy).
|
|
|
|
// For now we ignore this fact though, it's likely that it never happens.
|
|
|
|
// Albrecht, Sep 27, 2024
|
|
|
|
|
|
|
|
Fl::e_keysym = 0; // init: not used (zero) for scroll wheel events
|
2017-05-17 14:54:18 +03:00
|
|
|
set_event_xy(window);
|
2012-06-21 12:52:29 +04:00
|
|
|
Fl::e_dx = Fl::e_dy = 0;
|
2024-09-26 18:59:52 +03:00
|
|
|
|
|
|
|
if (mb == Button4 && !Fl::event_shift()) {
|
|
|
|
Fl::e_dy = -1; // up
|
2002-04-10 05:32:03 +04:00
|
|
|
event = FL_MOUSEWHEEL;
|
2024-09-26 18:59:52 +03:00
|
|
|
} else if (mb == Button5 && !Fl::event_shift()) {
|
|
|
|
Fl::e_dy = +1; // down
|
2002-04-10 05:32:03 +04:00
|
|
|
event = FL_MOUSEWHEEL;
|
2024-09-26 18:59:52 +03:00
|
|
|
} else if (mb == 6 || (mb == Button4 && Fl::event_shift())) {
|
2024-11-02 19:11:32 +03:00
|
|
|
Fl::e_dx = -1; // left
|
|
|
|
event = FL_MOUSEWHEEL;
|
2024-09-26 18:59:52 +03:00
|
|
|
} else if (mb == 7 || (mb == Button5 && Fl::event_shift())) {
|
2024-11-02 19:11:32 +03:00
|
|
|
Fl::e_dx = +1; // right
|
|
|
|
event = FL_MOUSEWHEEL;
|
2024-09-26 18:59:52 +03:00
|
|
|
} else if (mb < 4 || mb > 7) { // real mouse *buttons*, not scroll wheel
|
2024-11-02 19:11:32 +03:00
|
|
|
if (mb > 7) // 8 = back, 9 = forward
|
|
|
|
mb -= 4; // map to 4 and 5, resp.
|
|
|
|
Fl::e_keysym = FL_Button + mb;
|
|
|
|
Fl::e_state |= (FL_BUTTON1 << (mb-1)); // set button state
|
|
|
|
if (mb == 4) xbutton_state |= FL_BUTTON4; // save extra button state internally
|
|
|
|
if (mb == 5) xbutton_state |= FL_BUTTON5; // save extra button state internally
|
|
|
|
event = FL_PUSH;
|
|
|
|
checkdouble();
|
2024-09-26 18:59:52 +03:00
|
|
|
} else { // unknown button or shift combination
|
|
|
|
return 0;
|
2002-04-10 05:32:03 +04:00
|
|
|
}
|
|
|
|
|
2021-11-05 15:12:52 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
2021-11-05 15:12:52 +03:00
|
|
|
#endif // FLTK_CONSOLIDATE_MOTION
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-04-10 05:32:03 +04:00
|
|
|
break;
|
2024-09-26 18:59:52 +03:00
|
|
|
} // ButtonPress
|
|
|
|
|
|
|
|
// Mouse button release event: for details see ButtonPress above
|
|
|
|
|
|
|
|
case ButtonRelease: {
|
|
|
|
int mb = xevent.xbutton.button; // mouse button
|
|
|
|
switch (mb) { // figure out which real button this is
|
|
|
|
case 1: // left
|
|
|
|
case 2: // middle
|
|
|
|
case 3: // right
|
|
|
|
break; // continue
|
|
|
|
case 8: // side button 1 (back)
|
|
|
|
case 9: // side button 2 (forward)
|
|
|
|
mb -= 4; // map to 4 and 5, respectively
|
|
|
|
break; // continue
|
|
|
|
default: // unknown button or scroll wheel:
|
|
|
|
return 0; // don't send FL_RELEASE event
|
|
|
|
}
|
|
|
|
Fl::e_keysym = FL_Button + mb; // == FL_BUTTON1 .. FL_BUTTON5
|
|
|
|
set_event_xy(window);
|
|
|
|
|
|
|
|
Fl::e_state &= ~(FL_BUTTON1 << (mb-1));
|
|
|
|
if (mb == 4) xbutton_state &= ~FL_BUTTON4; // clear internal button state
|
|
|
|
if (mb == 5) xbutton_state &= ~FL_BUTTON5; // clear internal button state
|
|
|
|
event = FL_RELEASE;
|
|
|
|
|
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
|
|
|
fl_xmousewin = window;
|
|
|
|
#endif // FLTK_CONSOLIDATE_MOTION
|
|
|
|
in_a_window = true;
|
|
|
|
break;
|
|
|
|
} // ButtonRelease
|
2002-04-10 05:32:03 +04:00
|
|
|
|
2012-03-23 20:47:53 +04:00
|
|
|
case PropertyNotify:
|
|
|
|
if (xevent.xproperty.atom == fl_NET_WM_STATE) {
|
|
|
|
int fullscreen_state = 0;
|
2023-11-04 13:30:45 +03:00
|
|
|
int maximize_state = 0;
|
2012-03-23 20:47:53 +04:00
|
|
|
if (xevent.xproperty.state != PropertyDelete) {
|
|
|
|
unsigned long nitems;
|
|
|
|
unsigned long *words = 0;
|
2020-07-01 19:03:10 +03:00
|
|
|
if (0 == get_xwinprop(xid, fl_NET_WM_STATE, 64, &nitems, &words) ) {
|
2012-03-23 20:47:53 +04:00
|
|
|
for (unsigned long item = 0; item < nitems; item++) {
|
|
|
|
if (words[item] == fl_NET_WM_STATE_FULLSCREEN) {
|
|
|
|
fullscreen_state = 1;
|
|
|
|
}
|
2023-11-04 13:30:45 +03:00
|
|
|
if (words[item] == fl_NET_WM_STATE_MAXIMIZED_HORZ) {
|
|
|
|
maximize_state = 1;
|
|
|
|
}
|
2012-03-23 20:47:53 +04:00
|
|
|
}
|
|
|
|
}
|
2020-07-01 19:03:10 +03:00
|
|
|
if ( words ) { XFree(words); words = 0; }
|
2012-03-23 20:47:53 +04:00
|
|
|
}
|
2023-11-04 13:30:45 +03:00
|
|
|
Fl_Window_Driver::driver(window)->is_maximized(maximize_state);
|
2012-03-23 20:47:53 +04:00
|
|
|
if (window->fullscreen_active() && !fullscreen_state) {
|
|
|
|
window->_clear_fullscreen();
|
|
|
|
event = FL_FULLSCREEN;
|
|
|
|
}
|
|
|
|
if (!window->fullscreen_active() && fullscreen_state) {
|
|
|
|
window->_set_fullscreen();
|
|
|
|
event = FL_FULLSCREEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2002-04-10 05:32:03 +04:00
|
|
|
case MotionNotify:
|
2017-05-17 14:54:18 +03:00
|
|
|
set_event_xy(window);
|
2021-11-05 15:12:52 +03:00
|
|
|
in_a_window = true;
|
2021-11-05 14:46:21 +03:00
|
|
|
# if FLTK_CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
send_motion = fl_xmousewin = window;
|
|
|
|
return 0;
|
|
|
|
# else
|
|
|
|
event = FL_MOVE;
|
|
|
|
break;
|
|
|
|
# endif
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
case EnterNotify:
|
1998-12-29 17:08:07 +03:00
|
|
|
if (xevent.xcrossing.detail == NotifyInferior) break;
|
2023-01-13 23:16:17 +03:00
|
|
|
// XInstallColormap(fl_display, Fl_X::flx(window)->colormap);
|
2017-05-17 14:54:18 +03:00
|
|
|
set_event_xy(window);
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl::e_state = xevent.xcrossing.state << 16;
|
|
|
|
event = FL_ENTER;
|
2002-04-10 05:32:03 +04:00
|
|
|
|
2021-11-05 15:12:52 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
2021-11-05 15:12:52 +03:00
|
|
|
#endif // FLTK_CONSOLIDATE_MOTION
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2010-12-22 10:09:25 +03:00
|
|
|
{ XIMStyles *xim_styles = NULL;
|
2022-06-19 11:23:24 +03:00
|
|
|
if(!Fl_X11_Screen_Driver::xim_im || XGetIMValues(Fl_X11_Screen_Driver::xim_im, XNQueryInputStyle, &xim_styles, NULL, NULL)) {
|
|
|
|
Fl_X11_Screen_Driver::init_xim();
|
2010-12-22 10:09:25 +03:00
|
|
|
}
|
2010-12-22 12:22:38 +03:00
|
|
|
if (xim_styles) XFree(xim_styles);
|
2010-12-22 10:09:25 +03:00
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LeaveNotify:
|
1998-12-29 17:08:07 +03:00
|
|
|
if (xevent.xcrossing.detail == NotifyInferior) break;
|
2017-05-17 14:54:18 +03:00
|
|
|
set_event_xy(window);
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl::e_state = xevent.xcrossing.state << 16;
|
2021-11-05 15:12:52 +03:00
|
|
|
#if FLTK_CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = 0;
|
2021-11-05 15:12:52 +03:00
|
|
|
#endif // FLTK_CONSOLIDATE_MOTION
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = false; // make do_queued_events produce FL_LEAVE event
|
|
|
|
return 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2002-06-24 06:04:54 +04:00
|
|
|
// We cannot rely on the x,y position in the configure notify event.
|
|
|
|
// I now think this is an unavoidable problem with X: it is impossible
|
|
|
|
// for a window manager to prevent the "real" notify event from being
|
|
|
|
// sent when it resizes the contents, even though it can send an
|
|
|
|
// artificial event with the correct position afterwards (and some
|
|
|
|
// window managers do not send this fake event anyway)
|
|
|
|
// So anyway, do a round trip to find the correct x,y:
|
|
|
|
case MapNotify:
|
|
|
|
event = FL_SHOW;
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
case ConfigureNotify: {
|
2002-06-24 06:04:54 +04:00
|
|
|
if (window->parent()) break; // ignore child windows
|
2002-03-07 22:22:58 +03:00
|
|
|
|
2002-06-24 06:04:54 +04:00
|
|
|
// figure out where OS really put window
|
|
|
|
XWindowAttributes actual;
|
|
|
|
XGetWindowAttributes(fl_display, fl_xid(window), &actual);
|
|
|
|
Window cr; int X, Y, W = actual.width, H = actual.height;
|
|
|
|
XTranslateCoordinates(fl_display, fl_xid(window), actual.root,
|
|
|
|
0, 0, &X, &Y, &cr);
|
2017-07-30 19:21:57 +03:00
|
|
|
#if USE_XFT // detect when window centre changes screen
|
2017-05-17 14:54:18 +03:00
|
|
|
Fl_X11_Screen_Driver *d = (Fl_X11_Screen_Driver*)Fl::screen_driver();
|
|
|
|
Fl_X11_Window_Driver *wd = Fl_X11_Window_Driver::driver(window);
|
|
|
|
int olds = wd->screen_num();
|
2017-07-30 19:21:57 +03:00
|
|
|
int num = d->screen_num_unscaled(X+ actual.width/2, Y +actual.height/2);
|
|
|
|
if (num == -1) num = olds;
|
2017-05-17 14:54:18 +03:00
|
|
|
float s = d->scale(num);
|
|
|
|
if (num != olds) {
|
|
|
|
if (s != d->scale(olds) &&
|
|
|
|
!Fl_X11_Window_Driver::data_for_resize_window_between_screens_.busy &&
|
|
|
|
window->user_data() != &Fl_X11_Screen_Driver::transient_scale_display) {
|
|
|
|
Fl_X11_Window_Driver::data_for_resize_window_between_screens_.busy = true;
|
|
|
|
Fl_X11_Window_Driver::data_for_resize_window_between_screens_.screen = num;
|
|
|
|
// resize_after_screen_change() works also if called here, but calling it
|
|
|
|
// a second later gives a more pleasant user experience when moving windows between distinct screens
|
|
|
|
Fl::add_timeout(1, Fl_X11_Window_Driver::resize_after_screen_change, window);
|
2024-03-04 12:10:04 +03:00
|
|
|
} else if (!Fl_X11_Window_Driver::data_for_resize_window_between_screens_.busy)
|
|
|
|
wd->screen_num(num);
|
|
|
|
} else if (Fl_X11_Window_Driver::data_for_resize_window_between_screens_.busy) {
|
|
|
|
Fl::remove_timeout(Fl_X11_Window_Driver::resize_after_screen_change, window);
|
|
|
|
Fl_X11_Window_Driver::data_for_resize_window_between_screens_.busy = false;
|
2017-05-17 14:54:18 +03:00
|
|
|
}
|
2022-09-01 12:55:41 +03:00
|
|
|
#else // ! USE_XFT
|
|
|
|
Fl_Window_Driver::driver(window)->screen_num( Fl::screen_num(X, Y, W, H) );
|
2017-05-17 14:54:18 +03:00
|
|
|
#endif // USE_XFT
|
2002-06-24 06:04:54 +04:00
|
|
|
|
|
|
|
// tell Fl_Window about it and set flag to prevent echoing:
|
|
|
|
resize_bug_fix = window;
|
2020-07-01 19:03:10 +03:00
|
|
|
#if USE_XFT
|
2017-05-17 14:54:18 +03:00
|
|
|
if (!Fl_X11_Window_Driver::data_for_resize_window_between_screens_.busy &&
|
2017-06-10 09:10:37 +03:00
|
|
|
( ceil(W/s) != window->w() || ceil(H/s) != window->h() ) ) {
|
2020-11-25 11:21:27 +03:00
|
|
|
window->resize(rint(X/s), rint(Y/s), ceil(W/s), ceil(H/s));
|
2017-05-17 14:54:18 +03:00
|
|
|
} else {
|
2020-11-25 11:21:27 +03:00
|
|
|
window->position(rint(X/s), rint(Y/s));
|
2017-05-17 14:54:18 +03:00
|
|
|
}
|
|
|
|
#else
|
2002-06-24 06:04:54 +04:00
|
|
|
window->resize(X, Y, W, H);
|
2017-05-17 14:54:18 +03:00
|
|
|
#endif
|
2002-06-24 06:04:54 +04:00
|
|
|
break; // allow add_handler to do something too
|
|
|
|
}
|
2004-09-11 23:32:43 +04:00
|
|
|
|
|
|
|
case ReparentNotify: {
|
|
|
|
int xpos, ypos;
|
|
|
|
Window junk;
|
2011-01-16 01:47:30 +03:00
|
|
|
|
2010-11-17 14:28:58 +03:00
|
|
|
// on some systems, the ReparentNotify event is not handled as we would expect.
|
|
|
|
XErrorHandler oldHandler = XSetErrorHandler(catchXExceptions());
|
2004-09-11 23:32:43 +04:00
|
|
|
|
|
|
|
//ReparentNotify gives the new position of the window relative to
|
|
|
|
//the new parent. FLTK cares about the position on the root window.
|
|
|
|
XTranslateCoordinates(fl_display, xevent.xreparent.parent,
|
2009-03-07 18:15:29 +03:00
|
|
|
XRootWindow(fl_display, fl_screen),
|
|
|
|
xevent.xreparent.x, xevent.xreparent.y,
|
|
|
|
&xpos, &ypos, &junk);
|
2010-11-17 14:28:58 +03:00
|
|
|
XSetErrorHandler(oldHandler);
|
2004-09-11 23:32:43 +04:00
|
|
|
|
2004-11-20 07:18:44 +03:00
|
|
|
// tell Fl_Window about it and set flag to prevent echoing:
|
2010-11-17 14:28:58 +03:00
|
|
|
if ( !wasXExceptionRaised() ) {
|
|
|
|
resize_bug_fix = window;
|
2017-05-17 14:54:18 +03:00
|
|
|
#if USE_XFT
|
2018-05-12 12:36:36 +03:00
|
|
|
int ns = Fl_Window_Driver::driver(window)->screen_num();
|
2017-05-17 14:54:18 +03:00
|
|
|
float s = Fl::screen_driver()->scale(ns);
|
|
|
|
#else
|
|
|
|
float s = 1;
|
|
|
|
#endif
|
2020-11-25 11:21:27 +03:00
|
|
|
window->position(rint(xpos/s), rint(ypos/s));
|
2010-11-17 14:28:58 +03:00
|
|
|
}
|
2004-09-11 23:32:43 +04:00
|
|
|
break;
|
2021-01-29 02:06:34 +03:00
|
|
|
} // ReparentNotify
|
|
|
|
} // if (window) switch (xevent.type)
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2013-09-11 16:54:40 +04:00
|
|
|
#if HAVE_XFIXES
|
|
|
|
switch (xevent.type - xfixes_event_base) {
|
|
|
|
case XFixesSelectionNotify: {
|
|
|
|
// Someone feeding us bogus events?
|
|
|
|
if (!have_xfixes)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
XFixesSelectionNotifyEvent *selection_notify = (XFixesSelectionNotifyEvent *)&xevent;
|
|
|
|
|
|
|
|
if ((selection_notify->selection == XA_PRIMARY) && !fl_i_own_selection[0])
|
|
|
|
handle_clipboard_timestamp(0, selection_notify->selection_timestamp);
|
|
|
|
else if ((selection_notify->selection == CLIPBOARD) && !fl_i_own_selection[1])
|
|
|
|
handle_clipboard_timestamp(1, selection_notify->selection_timestamp);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
return Fl::handle(event, window);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-03-23 00:21:08 +03:00
|
|
|
void Fl_X11_Window_Driver::resize(int X,int Y,int W,int H) {
|
2022-08-29 19:54:18 +03:00
|
|
|
int is_a_rescale = Fl_Window::is_a_rescale();
|
|
|
|
int is_a_move = (X != x() || Y != y() || is_a_rescale);
|
|
|
|
int is_a_resize = (W != w() || H != h() || is_a_rescale);
|
2016-03-23 00:21:08 +03:00
|
|
|
int resize_from_program = (pWindow != resize_bug_fix);
|
1998-10-20 01:00:26 +04:00
|
|
|
if (!resize_from_program) resize_bug_fix = 0;
|
2016-03-23 00:21:08 +03:00
|
|
|
if (is_a_move && resize_from_program) force_position(1);
|
2004-11-20 16:52:47 +03:00
|
|
|
else if (!is_a_resize && !is_a_move) return;
|
1998-10-20 01:00:26 +04:00
|
|
|
if (is_a_resize) {
|
2023-10-17 10:37:01 +03:00
|
|
|
if (pWindow->as_double_window() && pWindow->parent()) {
|
|
|
|
if (W < 1) W = 1;
|
|
|
|
if (H < 1) H = 1;
|
|
|
|
}
|
2016-03-23 00:21:08 +03:00
|
|
|
pWindow->Fl_Group::resize(X,Y,W,H);
|
2022-03-15 08:42:06 +03:00
|
|
|
if (shown()) {
|
|
|
|
#if FLTK_USE_CAIRO
|
|
|
|
if (!pWindow->as_gl_window() && cairo_) {
|
|
|
|
float s = Fl::screen_driver()->scale(screen_num());
|
|
|
|
cairo_xlib_surface_set_size(cairo_get_target(cairo_), (W>0 ? int(W*s) : 1), (H>0 ? int(H*s) : 1));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
pWindow->redraw();
|
|
|
|
}
|
1998-10-20 01:00:26 +04:00
|
|
|
} else {
|
|
|
|
x(X); y(Y);
|
2022-06-19 11:23:24 +03:00
|
|
|
if (Fl_X11_Screen_Driver::xim_win && Fl::focus()) {
|
2022-01-08 19:08:39 +03:00
|
|
|
// Force the Input Method auxiliary window to move too.
|
|
|
|
Fl::focus()->handle(FL_FOCUS);
|
|
|
|
fl_set_spot(fl_font(), fl_size(), Fl::focus()->x(), Fl::focus()->y() + fl_size(), Fl::focus()->w(), Fl::focus()->h(), NULL);
|
|
|
|
}
|
1998-10-20 01:00:26 +04:00
|
|
|
}
|
2004-05-14 01:02:41 +04:00
|
|
|
|
2022-08-29 19:54:18 +03:00
|
|
|
if (is_a_rescale) size_range();
|
|
|
|
|
2016-03-24 04:20:08 +03:00
|
|
|
if (resize_from_program && shown()) {
|
2017-05-17 14:54:18 +03:00
|
|
|
float s = Fl::screen_driver()->scale(screen_num());
|
2000-01-11 11:20:02 +03:00
|
|
|
if (is_a_resize) {
|
2024-03-03 22:53:16 +03:00
|
|
|
if (!is_resizable()) pWindow->size_range(w(),h(),w(),h());
|
2004-05-16 02:58:19 +04:00
|
|
|
if (is_a_move) {
|
2020-11-25 11:21:27 +03:00
|
|
|
XMoveResizeWindow(fl_display, fl_xid(pWindow), rint(X*s), rint(Y*s), W>0 ? W*s : 1, H>0 ? H*s : 1);
|
2004-05-16 02:58:19 +04:00
|
|
|
} else {
|
2017-05-17 14:54:18 +03:00
|
|
|
XResizeWindow(fl_display, fl_xid(pWindow), W>0 ? W*s : 1, H>0 ? H*s : 1);
|
2004-05-16 02:58:19 +04:00
|
|
|
}
|
2005-12-14 04:03:13 +03:00
|
|
|
} else
|
2020-11-25 11:21:27 +03:00
|
|
|
XMoveWindow(fl_display, fl_xid(pWindow), rint(X*s), rint(Y*s));
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
2012-03-23 20:47:53 +04:00
|
|
|
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
|
|
|
|
#define _NET_WM_STATE_ADD 1 /* add/set property */
|
|
|
|
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
|
|
|
|
|
2014-06-11 13:10:53 +04:00
|
|
|
static void send_wm_event(Window wnd, Atom message,
|
|
|
|
unsigned long d0, unsigned long d1=0,
|
|
|
|
unsigned long d2=0, unsigned long d3=0,
|
|
|
|
unsigned long d4=0) {
|
2012-03-23 20:47:53 +04:00
|
|
|
XEvent e;
|
|
|
|
e.xany.type = ClientMessage;
|
|
|
|
e.xany.window = wnd;
|
2014-06-11 13:10:53 +04:00
|
|
|
e.xclient.message_type = message;
|
2012-03-23 20:47:53 +04:00
|
|
|
e.xclient.format = 32;
|
2014-06-11 13:10:53 +04:00
|
|
|
e.xclient.data.l[0] = d0;
|
|
|
|
e.xclient.data.l[1] = d1;
|
|
|
|
e.xclient.data.l[2] = d2;
|
|
|
|
e.xclient.data.l[3] = d3;
|
|
|
|
e.xclient.data.l[4] = d4;
|
2012-03-23 20:47:53 +04:00
|
|
|
XSendEvent(fl_display, RootWindow(fl_display, fl_screen),
|
|
|
|
0, SubstructureNotifyMask | SubstructureRedirectMask,
|
|
|
|
&e);
|
|
|
|
}
|
|
|
|
|
2014-06-11 13:10:53 +04:00
|
|
|
static void send_wm_state_event(Window wnd, int add, Atom prop) {
|
|
|
|
send_wm_event(wnd, fl_NET_WM_STATE,
|
|
|
|
add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE, prop);
|
|
|
|
}
|
|
|
|
|
2016-04-16 14:02:35 +03:00
|
|
|
int Fl_X11_Screen_Driver::ewmh_supported() {
|
2012-03-23 20:47:53 +04:00
|
|
|
static int result = -1;
|
|
|
|
|
|
|
|
if (result == -1) {
|
2014-11-04 20:26:47 +03:00
|
|
|
fl_open_display();
|
2012-03-23 20:47:53 +04:00
|
|
|
result = 0;
|
|
|
|
unsigned long nitems;
|
|
|
|
unsigned long *words = 0;
|
|
|
|
if (0 == get_xwinprop(XRootWindow(fl_display, fl_screen), fl_NET_SUPPORTING_WM_CHECK, 64,
|
|
|
|
&nitems, &words) && nitems == 1) {
|
|
|
|
Window child = words[0];
|
2013-09-20 07:36:02 +04:00
|
|
|
if ( words ) { XFree(words); words = 0; }
|
2012-03-23 20:47:53 +04:00
|
|
|
if (0 == get_xwinprop(child, fl_NET_SUPPORTING_WM_CHECK, 64,
|
2013-09-20 07:36:02 +04:00
|
|
|
&nitems, &words) ) {
|
|
|
|
if ( nitems == 1) result = (child == words[0]);
|
2012-03-23 20:47:53 +04:00
|
|
|
}
|
|
|
|
}
|
2013-10-09 15:46:36 +04:00
|
|
|
if ( words ) { XFree(words); words = 0; }
|
2012-03-23 20:47:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-09-02 17:28:53 +03:00
|
|
|
#if HAVE_XRENDER && (!FLTK_USE_CAIRO)
|
2016-04-16 12:01:19 +03:00
|
|
|
static int xrender_supported() {
|
|
|
|
int nop1, nop2;
|
|
|
|
fl_open_display();
|
|
|
|
return XRenderQueryExtension(fl_display, &nop1, &nop2);
|
|
|
|
}
|
|
|
|
#endif
|
2015-03-16 14:07:00 +03:00
|
|
|
|
2022-03-15 08:42:06 +03:00
|
|
|
#if ! FLTK_USE_CAIRO
|
2016-04-16 12:01:19 +03:00
|
|
|
char Fl_Xlib_Graphics_Driver::can_do_alpha_blending() {
|
|
|
|
#if HAVE_XRENDER
|
|
|
|
static char result = (char)xrender_supported();
|
2015-03-16 14:07:00 +03:00
|
|
|
return result;
|
2015-03-16 18:17:49 +03:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
2015-03-16 14:07:00 +03:00
|
|
|
}
|
2022-03-15 08:42:06 +03:00
|
|
|
#endif
|
2015-03-16 14:07:00 +03:00
|
|
|
|
2014-09-05 16:33:35 +04:00
|
|
|
extern Fl_Window *fl_xfocus;
|
|
|
|
|
2016-04-18 16:40:34 +03:00
|
|
|
void Fl_X11_Window_Driver::activate_window() {
|
|
|
|
Window w = fl_xid(pWindow);
|
2016-04-16 14:02:35 +03:00
|
|
|
if (!Fl_X11_Screen_Driver::ewmh_supported())
|
2014-09-05 16:33:35 +04:00
|
|
|
return;
|
|
|
|
|
2014-09-05 17:14:16 +04:00
|
|
|
Window prev = 0;
|
|
|
|
|
|
|
|
if (fl_xfocus) {
|
2023-01-13 23:16:17 +03:00
|
|
|
Fl_X *x = Fl_X::flx(fl_xfocus);
|
2014-09-05 17:14:16 +04:00
|
|
|
if (!x)
|
|
|
|
return;
|
|
|
|
prev = x->xid;
|
|
|
|
}
|
2014-09-05 16:33:35 +04:00
|
|
|
|
2023-12-13 21:07:30 +03:00
|
|
|
send_wm_event(w, fl_NET_ACTIVE_WINDOW,
|
|
|
|
1, // source: 1 = application
|
|
|
|
fl_event_time, // time of client's last user activity (STR 3396)
|
|
|
|
prev); // previously active window
|
2014-09-05 16:33:35 +04:00
|
|
|
}
|
|
|
|
|
2012-03-23 20:47:53 +04:00
|
|
|
/* Change an existing window to fullscreen */
|
2016-03-23 17:39:02 +03:00
|
|
|
void Fl_X11_Window_Driver::fullscreen_on() {
|
2022-09-23 15:25:08 +03:00
|
|
|
pWindow->_set_fullscreen();
|
2016-04-16 14:02:35 +03:00
|
|
|
if (Fl_X11_Screen_Driver::ewmh_supported()) {
|
2014-06-11 13:10:53 +04:00
|
|
|
int top, bottom, left, right;
|
2020-07-01 19:03:10 +03:00
|
|
|
|
2016-03-23 17:39:02 +03:00
|
|
|
top = fullscreen_screen_top();
|
|
|
|
bottom = fullscreen_screen_bottom();
|
|
|
|
left = fullscreen_screen_left();
|
|
|
|
right = fullscreen_screen_right();
|
2020-07-01 19:03:10 +03:00
|
|
|
|
2014-06-11 13:10:53 +04:00
|
|
|
if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
|
2017-05-17 14:54:18 +03:00
|
|
|
top = screen_num();
|
2014-06-11 13:10:53 +04:00
|
|
|
bottom = top;
|
|
|
|
left = top;
|
|
|
|
right = top;
|
|
|
|
}
|
2020-07-01 19:03:10 +03:00
|
|
|
|
2016-03-23 17:39:02 +03:00
|
|
|
send_wm_event(fl_xid(pWindow), fl_NET_WM_FULLSCREEN_MONITORS,
|
2014-06-11 13:10:53 +04:00
|
|
|
top, bottom, left, right);
|
2016-03-23 17:39:02 +03:00
|
|
|
send_wm_state_event(fl_xid(pWindow), 1, fl_NET_WM_STATE_FULLSCREEN);
|
2012-03-23 20:47:53 +04:00
|
|
|
} else {
|
|
|
|
hide();
|
|
|
|
show();
|
|
|
|
/* We want to grab the window, not a widget, so we cannot use Fl::grab */
|
2016-03-23 17:39:02 +03:00
|
|
|
XGrabKeyboard(fl_display, fl_xid(pWindow), 1, GrabModeAsync, GrabModeAsync, fl_event_time);
|
|
|
|
Fl::handle(FL_FULLSCREEN, pWindow);
|
2012-03-23 20:47:53 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-23 17:39:02 +03:00
|
|
|
void Fl_X11_Window_Driver::fullscreen_off(int X, int Y, int W, int H) {
|
2022-09-23 15:25:08 +03:00
|
|
|
pWindow->_clear_fullscreen();
|
2016-04-16 14:02:35 +03:00
|
|
|
if (Fl_X11_Screen_Driver::ewmh_supported()) {
|
2016-03-23 17:39:02 +03:00
|
|
|
send_wm_state_event(fl_xid(pWindow), 0, fl_NET_WM_STATE_FULLSCREEN);
|
2012-03-23 20:47:53 +04:00
|
|
|
} else {
|
|
|
|
/* The grab will be lost when the window is destroyed */
|
|
|
|
hide();
|
|
|
|
resize(X,Y,W,H);
|
|
|
|
show();
|
2016-03-23 17:39:02 +03:00
|
|
|
Fl::handle(FL_FULLSCREEN, pWindow);
|
2012-03-23 20:47:53 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-04 13:30:45 +03:00
|
|
|
|
|
|
|
void Fl_X11_Window_Driver::maximize() {
|
|
|
|
if (Fl_X11_Screen_Driver::ewmh_supported()) {
|
|
|
|
send_wm_event(fl_xid(pWindow), fl_NET_WM_STATE, _NET_WM_STATE_ADD,
|
|
|
|
fl_NET_WM_STATE_MAXIMIZED_VERT, fl_NET_WM_STATE_MAXIMIZED_HORZ);
|
|
|
|
} else {
|
|
|
|
*no_fullscreen_x() = x();
|
|
|
|
*no_fullscreen_y() = y();
|
|
|
|
*no_fullscreen_w() = w();
|
|
|
|
*no_fullscreen_h() = h();
|
|
|
|
int X,Y,W,H;
|
|
|
|
Fl::screen_work_area(X, Y, W, H, screen_num());
|
|
|
|
int width, height;
|
|
|
|
decorated_win_size(width, height);
|
|
|
|
int dw = (width - w());
|
|
|
|
int dh = (height - h() - dw);
|
|
|
|
resize(X + dw/2, Y + dh + dw/2, W - dw, H - dh - dw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fl_X11_Window_Driver::un_maximize() {
|
|
|
|
if (Fl_X11_Screen_Driver::ewmh_supported()) {
|
|
|
|
send_wm_event(fl_xid(pWindow), fl_NET_WM_STATE, _NET_WM_STATE_REMOVE,
|
|
|
|
fl_NET_WM_STATE_MAXIMIZED_VERT, fl_NET_WM_STATE_MAXIMIZED_HORZ);
|
|
|
|
} else {
|
|
|
|
resize(*no_fullscreen_x(), *no_fullscreen_y(),
|
|
|
|
*no_fullscreen_w(), *no_fullscreen_h());
|
|
|
|
*no_fullscreen_x() = 0;
|
|
|
|
*no_fullscreen_y() = 0;
|
|
|
|
*no_fullscreen_w() = 0;
|
|
|
|
*no_fullscreen_h() = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-23 20:47:53 +04:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
// A subclass of Fl_Window may call this to associate an X window it
|
|
|
|
// creates with the Fl_Window:
|
|
|
|
|
1998-12-07 16:34:27 +03:00
|
|
|
void fl_fix_focus(); // in Fl.cxx
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
Fl_X* Fl_X::set_xid(Fl_Window* win, Window winxid) {
|
2024-03-18 16:05:48 +03:00
|
|
|
if (!win->parent()) add_xid_vector(winxid); // store xid's of top-level FLTK windows
|
2016-03-08 00:45:55 +03:00
|
|
|
Fl_X *xp = new Fl_X;
|
2002-08-09 07:17:30 +04:00
|
|
|
xp->xid = winxid;
|
2018-05-12 12:36:36 +03:00
|
|
|
Fl_Window_Driver::driver(win)->other_xid = 0;
|
2023-01-13 23:16:17 +03:00
|
|
|
xp->w = win; win->flx_ = xp;
|
2002-08-09 07:17:30 +04:00
|
|
|
xp->next = Fl_X::first;
|
|
|
|
xp->region = 0;
|
2018-05-12 12:36:36 +03:00
|
|
|
Fl_Window_Driver::driver(win)->wait_for_expose_value = 1;
|
2002-08-09 07:17:30 +04:00
|
|
|
Fl_X::first = xp;
|
|
|
|
if (win->modal()) {Fl::modal_ = win; fl_fix_focus();}
|
|
|
|
return xp;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// More commonly a subclass calls this, because it hides the really
|
|
|
|
// ugly parts of X and sets all the stuff for a window that is set
|
|
|
|
// normally. The global variables like fl_show_iconic are so that
|
|
|
|
// subclasses of *that* class may change the behavior...
|
|
|
|
|
|
|
|
int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
|
|
|
|
|
|
|
|
static const int childEventMask = ExposureMask;
|
|
|
|
|
|
|
|
static const int XEventMask =
|
|
|
|
ExposureMask|StructureNotifyMask
|
|
|
|
|KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask
|
|
|
|
|ButtonPressMask|ButtonReleaseMask
|
|
|
|
|EnterWindowMask|LeaveWindowMask
|
2012-03-23 20:47:53 +04:00
|
|
|
|PropertyChangeMask
|
1998-10-06 22:21:25 +04:00
|
|
|
|PointerMotionMask;
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
|
1998-10-06 22:21:25 +04:00
|
|
|
{
|
|
|
|
Fl_Group::current(0); // get rid of very common user bug: forgot end()
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
int X = win->x();
|
|
|
|
int Y = win->y();
|
|
|
|
int W = win->w();
|
1998-11-12 17:14:57 +03:00
|
|
|
if (W <= 0) W = 1; // X don't like zero...
|
2002-08-09 07:17:30 +04:00
|
|
|
int H = win->h();
|
1998-11-12 17:14:57 +03:00
|
|
|
if (H <= 0) H = 1; // X don't like zero...
|
2002-08-09 07:17:30 +04:00
|
|
|
if (!win->parent() && !Fl::grab()) {
|
1999-11-04 21:35:12 +03:00
|
|
|
// center windows in case window manager does not do anything:
|
2004-11-20 07:18:44 +03:00
|
|
|
#ifdef FL_CENTER_WINDOWS
|
2011-11-29 18:41:33 +04:00
|
|
|
if (!win->force_position()) {
|
2005-04-01 00:31:39 +04:00
|
|
|
win->x(X = scr_x+(scr_w-W)/2);
|
|
|
|
win->y(Y = scr_y+(scr_h-H)/2);
|
1999-11-04 21:35:12 +03:00
|
|
|
}
|
2004-11-20 07:18:44 +03:00
|
|
|
#endif // FL_CENTER_WINDOWS
|
|
|
|
|
1998-11-12 17:14:57 +03:00
|
|
|
// force the window to be on-screen. Usually the X window manager
|
|
|
|
// does this, but a few don't, so we do it here for consistency:
|
2005-04-01 00:31:39 +04:00
|
|
|
int scr_x, scr_y, scr_w, scr_h;
|
2014-06-11 13:10:53 +04:00
|
|
|
Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y, W, H);
|
2005-04-01 00:31:39 +04:00
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
if (win->border()) {
|
1998-11-12 17:14:57 +03:00
|
|
|
// ensure border is on screen:
|
2009-03-22 22:21:34 +03:00
|
|
|
// (assume extremely minimal dimensions for this border)
|
1998-11-12 17:14:57 +03:00
|
|
|
const int top = 20;
|
|
|
|
const int left = 1;
|
|
|
|
const int right = 1;
|
|
|
|
const int bottom = 1;
|
2005-04-01 00:31:39 +04:00
|
|
|
if (X+W+right > scr_x+scr_w) X = scr_x+scr_w-right-W;
|
|
|
|
if (X-left < scr_x) X = scr_x+left;
|
|
|
|
if (Y+H+bottom > scr_y+scr_h) Y = scr_y+scr_h-bottom-H;
|
|
|
|
if (Y-top < scr_y) Y = scr_y+top;
|
1998-11-12 17:14:57 +03:00
|
|
|
}
|
|
|
|
// now insure contents are on-screen (more important than border):
|
2005-04-01 00:31:39 +04:00
|
|
|
if (X+W > scr_x+scr_w) X = scr_x+scr_w-W;
|
|
|
|
if (X < scr_x) X = scr_x;
|
|
|
|
if (Y+H > scr_y+scr_h) Y = scr_y+scr_h-H;
|
|
|
|
if (Y < scr_y) Y = scr_y;
|
1998-11-12 17:14:57 +03:00
|
|
|
}
|
|
|
|
|
2007-06-18 17:08:57 +04:00
|
|
|
// if the window is a subwindow and our parent is not mapped yet, we
|
|
|
|
// mark this window visible, so that mapping the parent at a later
|
|
|
|
// point in time will call this function again to finally map the subwindow.
|
2023-01-13 23:16:17 +03:00
|
|
|
if (win->parent() && !Fl_X::flx(win->window())) {
|
2007-06-18 12:43:37 +04:00
|
|
|
win->set_visible();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-11 13:10:53 +04:00
|
|
|
// Compute which screen(s) we should be on if we want to go fullscreen
|
|
|
|
int fullscreen_top, fullscreen_bottom, fullscreen_left, fullscreen_right;
|
|
|
|
|
|
|
|
fullscreen_top = win->fullscreen_screen_top;
|
|
|
|
fullscreen_bottom = win->fullscreen_screen_bottom;
|
|
|
|
fullscreen_left = win->fullscreen_screen_left;
|
|
|
|
fullscreen_right = win->fullscreen_screen_right;
|
|
|
|
|
|
|
|
if ((fullscreen_top < 0) || (fullscreen_bottom < 0) ||
|
|
|
|
(fullscreen_left < 0) || (fullscreen_right < 0)) {
|
|
|
|
fullscreen_top = Fl::screen_num(X, Y, W, H);
|
|
|
|
fullscreen_bottom = fullscreen_top;
|
|
|
|
fullscreen_left = fullscreen_top;
|
|
|
|
fullscreen_right = fullscreen_top;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
ulong root = win->parent() ?
|
|
|
|
fl_xid(win->window()) : RootWindow(fl_display, fl_screen);
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
XSetWindowAttributes attr;
|
|
|
|
int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
|
2002-08-09 07:17:30 +04:00
|
|
|
attr.event_mask = win->parent() ? childEventMask : XEventMask;
|
1998-10-06 22:21:25 +04:00
|
|
|
attr.colormap = colormap;
|
|
|
|
attr.border_pixel = 0;
|
|
|
|
attr.bit_gravity = 0; // StaticGravity;
|
2022-12-30 22:53:54 +03:00
|
|
|
if (win->override()) {
|
2001-10-18 22:53:20 +04:00
|
|
|
attr.override_redirect = 1;
|
|
|
|
attr.save_under = 1;
|
|
|
|
mask |= CWOverrideRedirect | CWSaveUnder;
|
|
|
|
} else attr.override_redirect = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
if (Fl::grab()) {
|
|
|
|
attr.save_under = 1; mask |= CWSaveUnder;
|
2002-08-09 07:17:30 +04:00
|
|
|
if (!win->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2012-03-23 20:47:53 +04:00
|
|
|
// For the non-EWMH fullscreen case, we cannot use the code above,
|
|
|
|
// since we do not want save_under, do not want to turn off the
|
2020-07-01 19:03:10 +03:00
|
|
|
// border, and cannot grab without an existing window. Besides,
|
|
|
|
// there is no clear_override().
|
2016-04-16 14:02:35 +03:00
|
|
|
if (win->fullscreen_active() && !Fl_X11_Screen_Driver::ewmh_supported()) {
|
2014-06-11 13:10:53 +04:00
|
|
|
int sx, sy, sw, sh;
|
2012-03-23 20:47:53 +04:00
|
|
|
attr.override_redirect = 1;
|
|
|
|
mask |= CWOverrideRedirect;
|
2014-06-11 13:10:53 +04:00
|
|
|
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_left);
|
|
|
|
X = sx;
|
|
|
|
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_right);
|
|
|
|
W = sx + sw - X;
|
|
|
|
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_top);
|
|
|
|
Y = sy;
|
|
|
|
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_bottom);
|
|
|
|
H = sy + sh - Y;
|
2012-03-23 20:47:53 +04:00
|
|
|
}
|
|
|
|
|
2022-02-22 21:55:11 +03:00
|
|
|
#ifdef ENABLE_BOXCHEAT
|
1998-10-06 22:21:25 +04:00
|
|
|
if (fl_background_pixel >= 0) {
|
|
|
|
attr.background_pixel = fl_background_pixel;
|
|
|
|
fl_background_pixel = -1;
|
|
|
|
mask |= CWBackPixel;
|
|
|
|
}
|
2022-02-22 21:55:11 +03:00
|
|
|
#endif // (ENABLE_BOXCHEAT)
|
|
|
|
|
2017-05-17 14:54:18 +03:00
|
|
|
float s = 1;
|
|
|
|
#if USE_XFT
|
|
|
|
//compute adequate screen where to put the window
|
|
|
|
int nscreen = 0;
|
|
|
|
if (win->parent()) {
|
2018-05-12 12:36:36 +03:00
|
|
|
nscreen = Fl_Window_Driver::driver(win->top_window())->screen_num();
|
2017-05-17 14:54:18 +03:00
|
|
|
} else if (win->force_position() && Fl_X11_Window_Driver::driver(win)->screen_num_ >= 0) {
|
2018-05-12 12:36:36 +03:00
|
|
|
nscreen = Fl_Window_Driver::driver(win)->screen_num();
|
2017-05-17 14:54:18 +03:00
|
|
|
} else {
|
|
|
|
Fl_Window *hint = Fl::first_window();
|
|
|
|
if (hint) {
|
2018-05-12 12:36:36 +03:00
|
|
|
nscreen = Fl_Window_Driver::driver(hint->top_window())->screen_num();
|
2017-05-17 14:54:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Fl_X11_Window_Driver::driver(win)->screen_num(nscreen);
|
|
|
|
s = Fl::screen_driver()->scale(nscreen);
|
2022-02-23 01:28:04 +03:00
|
|
|
// if (!win->parent()) printf("win creation on screen #%d\n", nscreen);
|
2017-05-17 14:54:18 +03:00
|
|
|
#endif
|
2002-08-09 07:17:30 +04:00
|
|
|
Fl_X* xp =
|
|
|
|
set_xid(win, XCreateWindow(fl_display,
|
2009-03-07 18:15:29 +03:00
|
|
|
root,
|
2020-11-25 11:21:27 +03:00
|
|
|
rint(X*s), rint(Y*s), W*s, H*s,
|
2009-03-07 18:15:29 +03:00
|
|
|
0, // borderwidth
|
|
|
|
visual->depth,
|
|
|
|
InputOutput,
|
|
|
|
visual->visual,
|
|
|
|
mask, &attr));
|
2000-04-25 11:48:07 +04:00
|
|
|
int showit = 1;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2015-04-20 15:02:10 +03:00
|
|
|
// Set WM_CLIENT_MACHINE and WM_LOCALE_NAME
|
|
|
|
XSetWMProperties(fl_display, xp->xid, NULL, NULL, NULL, 0, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
// Set _NET_WM_PID
|
|
|
|
long pid;
|
|
|
|
pid = getpid();
|
|
|
|
XChangeProperty(fl_display, xp->xid, fl_NET_WM_PID,
|
2020-07-01 19:03:10 +03:00
|
|
|
XA_CARDINAL, 32, 0, (unsigned char *)&pid, 1);
|
2015-04-20 15:02:10 +03:00
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
if (!win->parent() && !attr.override_redirect) {
|
1998-10-06 22:21:25 +04:00
|
|
|
// Communicate all kinds 'o junk to the X Window Manager:
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
win->label(win->label(), win->iconlabel());
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
XChangeProperty(fl_display, xp->xid, WM_PROTOCOLS,
|
2009-03-07 18:15:29 +03:00
|
|
|
XA_ATOM, 32, 0, (uchar*)&WM_DELETE_WINDOW, 1);
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
// send size limits and border:
|
2016-04-19 20:34:15 +03:00
|
|
|
Fl_X11_Window_Driver::driver(win)->sendxjunk();
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
// set the class property, which controls the icon used:
|
2002-08-09 07:17:30 +04:00
|
|
|
if (win->xclass()) {
|
1998-10-06 22:21:25 +04:00
|
|
|
char buffer[1024];
|
2011-10-01 19:53:57 +04:00
|
|
|
const char *xclass = win->xclass();
|
|
|
|
const int len = strlen(xclass);
|
|
|
|
// duplicate the xclass string for use as XA_WM_CLASS
|
|
|
|
strcpy(buffer, xclass);
|
|
|
|
strcpy(buffer + len + 1, xclass);
|
1998-10-06 22:21:25 +04:00
|
|
|
// create the capitalized version:
|
2011-10-01 19:53:57 +04:00
|
|
|
buffer[len + 1] = toupper(buffer[len + 1]);
|
|
|
|
if (buffer[len + 1] == 'X')
|
|
|
|
buffer[len + 2] = toupper(buffer[len + 2]);
|
2002-08-09 07:17:30 +04:00
|
|
|
XChangeProperty(fl_display, xp->xid, XA_WM_CLASS, XA_STRING, 8, 0,
|
2011-10-01 19:53:57 +04:00
|
|
|
(unsigned char *)buffer, len * 2 + 2);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
if (win->non_modal() && xp->next && !fl_disable_transient_for) {
|
1998-10-06 22:21:25 +04:00
|
|
|
// find some other window to be "transient for":
|
2002-08-09 07:17:30 +04:00
|
|
|
Fl_Window* wp = xp->next->w;
|
|
|
|
while (wp->parent()) wp = wp->window();
|
|
|
|
XSetTransientForHint(fl_display, xp->xid, fl_xid(wp));
|
|
|
|
if (!wp->visible()) showit = 0; // guess that wm will not show it
|
2014-06-11 18:09:28 +04:00
|
|
|
if (win->modal()) {
|
|
|
|
Atom net_wm_state = XInternAtom (fl_display, "_NET_WM_STATE", 0);
|
|
|
|
Atom net_wm_state_skip_taskbar = XInternAtom (fl_display, "_NET_WM_STATE_MODAL", 0);
|
|
|
|
XChangeProperty (fl_display, xp->xid, net_wm_state, XA_ATOM, 32,
|
|
|
|
PropModeAppend, (unsigned char*) &net_wm_state_skip_taskbar, 1);
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2011-01-16 01:47:30 +03:00
|
|
|
|
2005-08-09 03:31:41 +04:00
|
|
|
// Make sure that borderless windows do not show in the task bar
|
|
|
|
if (!win->border()) {
|
|
|
|
Atom net_wm_state = XInternAtom (fl_display, "_NET_WM_STATE", 0);
|
|
|
|
Atom net_wm_state_skip_taskbar = XInternAtom (fl_display, "_NET_WM_STATE_SKIP_TASKBAR", 0);
|
2008-09-11 03:56:49 +04:00
|
|
|
XChangeProperty (fl_display, xp->xid, net_wm_state, XA_ATOM, 32,
|
2005-08-09 03:31:41 +04:00
|
|
|
PropModeAppend, (unsigned char*) &net_wm_state_skip_taskbar, 1);
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2012-03-23 20:47:53 +04:00
|
|
|
// If asked for, create fullscreen
|
2016-04-16 14:02:35 +03:00
|
|
|
if (win->fullscreen_active() && Fl_X11_Screen_Driver::ewmh_supported()) {
|
2014-06-11 13:10:53 +04:00
|
|
|
unsigned long data[4];
|
|
|
|
data[0] = fullscreen_top;
|
|
|
|
data[1] = fullscreen_bottom;
|
|
|
|
data[2] = fullscreen_left;
|
|
|
|
data[3] = fullscreen_right;
|
|
|
|
XChangeProperty (fl_display, xp->xid, fl_NET_WM_FULLSCREEN_MONITORS, XA_ATOM, 32,
|
|
|
|
PropModeReplace, (unsigned char*) data, 4);
|
2012-03-23 20:47:53 +04:00
|
|
|
XChangeProperty (fl_display, xp->xid, fl_NET_WM_STATE, XA_ATOM, 32,
|
|
|
|
PropModeAppend, (unsigned char*) &fl_NET_WM_STATE_FULLSCREEN, 1);
|
|
|
|
}
|
|
|
|
|
2002-03-25 05:36:59 +03:00
|
|
|
// Make it receptive to DnD:
|
2024-06-15 12:33:44 +03:00
|
|
|
Atom version = 5; // max. XDND protocol version we understand
|
2002-08-09 07:17:30 +04:00
|
|
|
XChangeProperty(fl_display, xp->xid, fl_XdndAware,
|
2009-03-07 18:15:29 +03:00
|
|
|
XA_ATOM, sizeof(int)*8, 0, (unsigned char*)&version, 1);
|
2002-03-25 05:36:59 +03:00
|
|
|
|
2003-04-03 08:28:15 +04:00
|
|
|
XWMHints *hints = XAllocWMHints();
|
|
|
|
hints->input = True;
|
|
|
|
hints->flags = InputHint;
|
2023-11-17 18:55:37 +03:00
|
|
|
if (Fl_Window::show_next_window_iconic()) {
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->flags |= StateHint;
|
|
|
|
hints->initial_state = IconicState;
|
2023-11-17 18:55:37 +03:00
|
|
|
Fl_Window::show_next_window_iconic(0);
|
2000-04-25 11:48:07 +04:00
|
|
|
showit = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2022-10-08 18:59:06 +03:00
|
|
|
if (Fl_X11_Window_Driver::driver(win)->icon_ &&
|
|
|
|
Fl_X11_Window_Driver::driver(win)->icon_->legacy_icon) {
|
2016-04-19 20:34:15 +03:00
|
|
|
hints->icon_pixmap = (Pixmap)Fl_X11_Window_Driver::driver(win)->icon_->legacy_icon;
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->flags |= IconPixmapHint;
|
1999-01-13 18:45:50 +03:00
|
|
|
}
|
2003-04-03 08:28:15 +04:00
|
|
|
XSetWMHints(fl_display, xp->xid, hints);
|
|
|
|
XFree(hints);
|
2014-06-16 15:39:32 +04:00
|
|
|
|
2016-04-19 20:34:15 +03:00
|
|
|
Fl_X11_Window_Driver::driver(win)->set_icons();
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2010-04-07 00:20:18 +04:00
|
|
|
|
2010-06-15 11:49:26 +04:00
|
|
|
// set the window type for menu and tooltip windows to avoid animations (compiz)
|
|
|
|
if (win->menu_window() || win->tooltip_window()) {
|
|
|
|
Atom net_wm_type = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False);
|
|
|
|
Atom net_wm_type_kind = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_MENU", False);
|
2010-06-15 12:20:15 +04:00
|
|
|
XChangeProperty(fl_display, xp->xid, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_type_kind, 1);
|
2010-06-15 11:49:26 +04:00
|
|
|
}
|
|
|
|
|
2013-09-11 16:54:40 +04:00
|
|
|
#if HAVE_XFIXES
|
|
|
|
// register for clipboard change notifications
|
|
|
|
if (have_xfixes && !win->parent()) {
|
|
|
|
XFixesSelectSelectionInput(fl_display, xp->xid, XA_PRIMARY,
|
|
|
|
XFixesSetSelectionOwnerNotifyMask);
|
|
|
|
XFixesSelectSelectionInput(fl_display, xp->xid, CLIPBOARD,
|
|
|
|
XFixesSetSelectionOwnerNotifyMask);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-02-23 12:21:27 +03:00
|
|
|
if (win->shape()) {
|
2016-04-19 20:34:15 +03:00
|
|
|
Fl_X11_Window_Driver::driver(win)->combine_mask();
|
2014-08-27 15:55:57 +04:00
|
|
|
}
|
2002-08-09 07:17:30 +04:00
|
|
|
XMapWindow(fl_display, xp->xid);
|
2000-04-25 11:48:07 +04:00
|
|
|
if (showit) {
|
2002-08-09 07:17:30 +04:00
|
|
|
win->set_visible();
|
2004-12-03 06:14:17 +03:00
|
|
|
int old_event = Fl::e_number;
|
|
|
|
win->handle(Fl::e_number = FL_SHOW); // get child windows to appear
|
|
|
|
Fl::e_number = old_event;
|
2002-08-09 07:17:30 +04:00
|
|
|
win->redraw();
|
2000-04-25 11:48:07 +04:00
|
|
|
}
|
2012-03-23 20:47:53 +04:00
|
|
|
|
|
|
|
// non-EWMH fullscreen case, need grab
|
2016-04-16 14:02:35 +03:00
|
|
|
if (win->fullscreen_active() && !Fl_X11_Screen_Driver::ewmh_supported()) {
|
2012-03-23 20:47:53 +04:00
|
|
|
XGrabKeyboard(fl_display, xp->xid, 1, GrabModeAsync, GrabModeAsync, fl_event_time);
|
|
|
|
}
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Send X window stuff that can be changed over time:
|
|
|
|
|
2016-04-18 16:17:09 +03:00
|
|
|
void Fl_X11_Window_Driver::sendxjunk() {
|
|
|
|
Fl_Window *w = pWindow;
|
2022-12-30 22:53:54 +03:00
|
|
|
if (w->parent() || w->override()) return; // it's not a window manager window!
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2003-04-03 08:28:15 +04:00
|
|
|
XSizeHints *hints = XAllocSizeHints();
|
1999-05-06 10:20:47 +04:00
|
|
|
// memset(&hints, 0, sizeof(hints)); jreiser suggestion to fix purify?
|
2017-05-17 14:54:18 +03:00
|
|
|
float s = Fl::screen_driver()->scale(screen_num());
|
|
|
|
|
2024-05-27 12:34:34 +03:00
|
|
|
int minw, minh, maxw, maxh, dw, dh, aspect;
|
|
|
|
pWindow->get_size_range(&minw, &minh, &maxw, &maxh, &dw, &dh, &aspect);
|
|
|
|
hints->min_width = s * minw;
|
|
|
|
hints->min_height = s * minh;
|
|
|
|
hints->max_width = s * maxw;
|
|
|
|
hints->max_height = s * maxh;
|
2017-05-17 14:54:18 +03:00
|
|
|
if (int(s) == s) { // use win size increment value only if scale is an integer. Is it possible to do better?
|
2024-05-27 12:34:34 +03:00
|
|
|
hints->width_inc = s * dw;
|
|
|
|
hints->height_inc = s * dh;
|
2017-05-17 14:54:18 +03:00
|
|
|
} else {
|
2022-02-23 01:28:04 +03:00
|
|
|
hints->width_inc = 0;
|
2017-05-17 14:54:18 +03:00
|
|
|
hints->height_inc = 0;
|
|
|
|
}
|
2020-07-01 19:03:10 +03:00
|
|
|
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->win_gravity = StaticGravity;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
// see the file /usr/include/X11/Xm/MwmUtil.h:
|
|
|
|
// fill all fields to avoid bugs in kwm and perhaps other window managers:
|
|
|
|
// 0, MWM_FUNC_ALL, MWM_DECOR_ALL
|
|
|
|
long prop[5] = {0, 1, 1, 0, 0};
|
|
|
|
|
2003-04-03 08:28:15 +04:00
|
|
|
if (hints->min_width != hints->max_width ||
|
|
|
|
hints->min_height != hints->max_height) { // resizable
|
|
|
|
hints->flags = PMinSize|PWinGravity;
|
|
|
|
if (hints->max_width >= hints->min_width ||
|
2009-03-07 18:15:29 +03:00
|
|
|
hints->max_height >= hints->min_height) {
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->flags = PMinSize|PMaxSize|PWinGravity;
|
1998-10-06 22:21:25 +04:00
|
|
|
// unfortunately we can't set just one maximum size. Guess a
|
|
|
|
// value for the other one. Some window managers will make the
|
|
|
|
// window fit on screen when maximized, others will put it off screen:
|
2017-05-17 14:54:18 +03:00
|
|
|
if (hints->max_width < hints->min_width) hints->max_width = Fl::w()*s;
|
|
|
|
if (hints->max_height < hints->min_height) hints->max_height = Fl::h()*s;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2003-04-03 08:28:15 +04:00
|
|
|
if (hints->width_inc && hints->height_inc) hints->flags |= PResizeInc;
|
2024-05-27 12:34:34 +03:00
|
|
|
if (aspect) {
|
1998-10-06 22:21:25 +04:00
|
|
|
// stupid X! It could insist that the corner go on the
|
|
|
|
// straight line between min and max...
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->min_aspect.x = hints->max_aspect.x = hints->min_width;
|
|
|
|
hints->min_aspect.y = hints->max_aspect.y = hints->min_height;
|
|
|
|
hints->flags |= PAspect;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
} else { // not resizable:
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->flags = PMinSize|PMaxSize|PWinGravity;
|
1998-10-06 22:21:25 +04:00
|
|
|
prop[0] = 1; // MWM_HINTS_FUNCTIONS
|
|
|
|
prop[1] = 1|2|16; // MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE
|
|
|
|
}
|
|
|
|
|
2016-04-18 16:17:09 +03:00
|
|
|
if (force_position()) {
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->flags |= USPosition;
|
2017-05-17 14:54:18 +03:00
|
|
|
hints->x = s*w->x();
|
|
|
|
hints->y = s*w->y();
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!w->border()) {
|
|
|
|
prop[0] |= 2; // MWM_HINTS_DECORATIONS
|
|
|
|
prop[2] = 0; // no decorations
|
|
|
|
}
|
|
|
|
|
2016-04-18 16:17:09 +03:00
|
|
|
XSetWMNormalHints(fl_display, fl_xid(w), hints);
|
|
|
|
XChangeProperty(fl_display, fl_xid(w),
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_MOTIF_WM_HINTS, fl_MOTIF_WM_HINTS,
|
|
|
|
32, 0, (unsigned char *)prop, 5);
|
2003-04-03 08:28:15 +04:00
|
|
|
XFree(hints);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-06-16 15:39:32 +04:00
|
|
|
static unsigned long *default_net_wm_icons = 0L;
|
|
|
|
static size_t default_net_wm_icons_size = 0;
|
|
|
|
|
2016-08-20 20:09:28 +03:00
|
|
|
// Note: icons[] *must* contain at least <count> valid image pointers (!NULL),
|
|
|
|
// but: <count> *may* be 0
|
2014-06-16 15:39:32 +04:00
|
|
|
static void icons_to_property(const Fl_RGB_Image *icons[], int count,
|
|
|
|
unsigned long **property, size_t *len) {
|
|
|
|
size_t sz;
|
|
|
|
unsigned long *data;
|
|
|
|
|
|
|
|
sz = 0;
|
|
|
|
for (int i = 0;i < count;i++)
|
2022-09-12 12:07:03 +03:00
|
|
|
sz += 2 + icons[i]->data_w() * icons[i]->data_h();
|
2014-06-16 15:39:32 +04:00
|
|
|
|
|
|
|
// FIXME: Might want to sort the icons
|
|
|
|
|
|
|
|
*property = data = new unsigned long[sz];
|
|
|
|
*len = sz;
|
|
|
|
|
|
|
|
for (int i = 0;i < count;i++) {
|
|
|
|
const Fl_RGB_Image *image;
|
|
|
|
|
|
|
|
image = icons[i];
|
2022-09-17 10:03:34 +03:00
|
|
|
bool need_delete = false;
|
|
|
|
if (image->w() != image->data_w() || image->h() != image->data_h()) {
|
|
|
|
image = (Fl_RGB_Image*)image->copy();
|
|
|
|
need_delete = true;
|
|
|
|
}
|
2014-06-16 15:39:32 +04:00
|
|
|
|
2022-09-12 12:07:03 +03:00
|
|
|
data[0] = image->data_w();
|
|
|
|
data[1] = image->data_h();
|
2014-06-16 15:39:32 +04:00
|
|
|
data += 2;
|
|
|
|
|
2022-09-12 12:07:03 +03:00
|
|
|
const int extra_data = image->ld() ? (image->ld() - image->data_w() * image->d()) : 0;
|
2016-10-14 19:35:52 +03:00
|
|
|
|
2014-06-16 15:39:32 +04:00
|
|
|
const uchar *in = (const uchar*)*image->data();
|
2022-09-12 12:07:03 +03:00
|
|
|
for (int y = 0; y < image->data_h(); y++) {
|
|
|
|
for (int x = 0; x < image->data_w(); x++) {
|
2014-06-16 15:39:32 +04:00
|
|
|
switch (image->d()) {
|
|
|
|
case 1:
|
|
|
|
*data = ( 0xff<<24) | (in[0]<<16) | (in[0]<<8) | in[0];
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
*data = (in[1]<<24) | (in[0]<<16) | (in[0]<<8) | in[0];
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
*data = ( 0xff<<24) | (in[0]<<16) | (in[1]<<8) | in[2];
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
*data = (in[3]<<24) | (in[0]<<16) | (in[1]<<8) | in[2];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
in += image->d();
|
|
|
|
data++;
|
|
|
|
}
|
2016-10-14 19:35:52 +03:00
|
|
|
in += extra_data;
|
2014-06-16 15:39:32 +04:00
|
|
|
}
|
2022-09-17 10:03:34 +03:00
|
|
|
if (need_delete) delete image;
|
2014-06-16 15:39:32 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-25 20:47:29 +03:00
|
|
|
void Fl_X11_Screen_Driver::default_icons(const Fl_RGB_Image *icons[], int count) {
|
2014-06-16 15:39:32 +04:00
|
|
|
if (default_net_wm_icons) {
|
|
|
|
delete [] default_net_wm_icons;
|
|
|
|
default_net_wm_icons = 0L;
|
|
|
|
default_net_wm_icons_size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
icons_to_property(icons, count,
|
|
|
|
&default_net_wm_icons, &default_net_wm_icons_size);
|
|
|
|
}
|
|
|
|
|
2016-04-16 16:15:49 +03:00
|
|
|
void Fl_X11_Window_Driver::set_icons() {
|
2014-06-16 15:39:32 +04:00
|
|
|
unsigned long *net_wm_icons;
|
|
|
|
size_t net_wm_icons_size;
|
|
|
|
|
2022-10-08 18:59:06 +03:00
|
|
|
if (icon_ && icon_->count) {
|
2016-04-16 16:15:49 +03:00
|
|
|
icons_to_property((const Fl_RGB_Image **)icon_->icons, icon_->count,
|
2014-06-16 15:39:32 +04:00
|
|
|
&net_wm_icons, &net_wm_icons_size);
|
|
|
|
} else {
|
|
|
|
net_wm_icons = default_net_wm_icons;
|
|
|
|
net_wm_icons_size = default_net_wm_icons_size;
|
|
|
|
}
|
|
|
|
|
2016-04-16 16:15:49 +03:00
|
|
|
XChangeProperty (fl_display, fl_xid(pWindow), fl_NET_WM_ICON, XA_CARDINAL, 32,
|
2014-06-16 15:39:32 +04:00
|
|
|
PropModeReplace, (unsigned char*) net_wm_icons, net_wm_icons_size);
|
|
|
|
|
2022-10-08 18:59:06 +03:00
|
|
|
if (icon_ && icon_->count) {
|
2014-06-16 15:39:32 +04:00
|
|
|
delete [] net_wm_icons;
|
|
|
|
net_wm_icons = 0L;
|
|
|
|
net_wm_icons_size = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
2020-11-22 12:16:04 +03:00
|
|
|
#if ! HAVE_XCURSOR
|
|
|
|
static void cache_pixmap_cursor(Fl_Cursor c, Cursor& cursor, Fl_Window *pWindow, Cursor& result) {
|
|
|
|
if (cursor != None) { // already cached?
|
|
|
|
result = cursor;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#define CURSORSIZE 16
|
|
|
|
#define HOTXY 7
|
|
|
|
static struct TableEntry {
|
|
|
|
uchar bits[CURSORSIZE*CURSORSIZE/8];
|
|
|
|
uchar mask[CURSORSIZE*CURSORSIZE/8];
|
|
|
|
Cursor cursor;
|
|
|
|
} table[] = {
|
|
|
|
{{ // FL_CURSOR_NWSE
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x38, 0x00, 0x78, 0x00,
|
|
|
|
0xe8, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x1c,
|
|
|
|
0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
{
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x7c, 0x00, 0xfc, 0x00,
|
|
|
|
0xfc, 0x01, 0xec, 0x03, 0xc0, 0x37, 0x80, 0x3f, 0x00, 0x3f, 0x00, 0x3e,
|
|
|
|
0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00}},
|
|
|
|
{{ // FL_CURSOR_NESW
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1e,
|
|
|
|
0x00, 0x17, 0x80, 0x03, 0xc0, 0x01, 0xe8, 0x00, 0x78, 0x00, 0x38, 0x00,
|
|
|
|
0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
{
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3f,
|
|
|
|
0x80, 0x3f, 0xc0, 0x37, 0xec, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0x7c, 0x00,
|
|
|
|
0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
|
|
|
{{0}, {0}} // FL_CURSOR_NONE & unknown
|
|
|
|
};
|
|
|
|
|
|
|
|
Cursor xc = None;
|
|
|
|
if (c >= FL_CURSOR_NWSE) {
|
|
|
|
TableEntry *q = (c > FL_CURSOR_NESW) ? table+2 : table+(c-FL_CURSOR_NWSE);
|
|
|
|
if (!(q->cursor)) {
|
|
|
|
XColor dummy = { 0 };
|
|
|
|
Pixmap p = XCreateBitmapFromData(fl_display,
|
|
|
|
RootWindow(fl_display, fl_screen), (const char*)(q->bits), CURSORSIZE, CURSORSIZE);
|
|
|
|
Pixmap m = XCreateBitmapFromData(fl_display,
|
|
|
|
RootWindow(fl_display, fl_screen), (const char*)(q->mask), CURSORSIZE, CURSORSIZE);
|
|
|
|
q->cursor = XCreatePixmapCursor(fl_display, p,m,&dummy, &dummy, HOTXY, HOTXY);
|
|
|
|
XFreePixmap(fl_display, m);
|
|
|
|
XFreePixmap(fl_display, p);
|
|
|
|
}
|
|
|
|
xc = q->cursor;
|
|
|
|
}
|
|
|
|
XColor fgc;
|
|
|
|
uchar r,g,b;
|
|
|
|
// hardcoded colors (legacy)
|
|
|
|
Fl_Color fg = FL_WHITE;
|
|
|
|
Fl_Color bg = FL_BLACK;
|
|
|
|
Fl::get_color(fg,r,g,b);
|
|
|
|
fgc.red = r<<8; fgc.green = g<<8; fgc.blue = b<<8;
|
|
|
|
XColor bgc;
|
|
|
|
Fl::get_color(bg,r,g,b);
|
|
|
|
bgc.red = r<<8; bgc.green = g<<8; bgc.blue = b<<8;
|
|
|
|
XRecolorCursor(fl_display, xc, &fgc, &bgc);
|
|
|
|
result = xc;
|
|
|
|
}
|
|
|
|
#endif // ! HAVE_XCURSOR
|
|
|
|
|
2016-04-18 17:31:07 +03:00
|
|
|
int Fl_X11_Window_Driver::set_cursor(Fl_Cursor c) {
|
2014-08-29 15:23:44 +04:00
|
|
|
|
|
|
|
/* The cursors are cached, because creating one takes 0.5ms including
|
|
|
|
opening, reading, and closing theme files. They are kept until program
|
|
|
|
exit by design, which valgrind will note as reachable. */
|
|
|
|
static Cursor xc_arrow = None;
|
|
|
|
static Cursor xc_cross = None;
|
|
|
|
static Cursor xc_wait = None;
|
|
|
|
static Cursor xc_insert = None;
|
|
|
|
static Cursor xc_hand = None;
|
|
|
|
static Cursor xc_help = None;
|
|
|
|
static Cursor xc_move = None;
|
|
|
|
static Cursor xc_ns = None;
|
|
|
|
static Cursor xc_we = None;
|
|
|
|
static Cursor xc_ne = None;
|
|
|
|
static Cursor xc_n = None;
|
|
|
|
static Cursor xc_nw = None;
|
|
|
|
static Cursor xc_e = None;
|
|
|
|
static Cursor xc_w = None;
|
|
|
|
static Cursor xc_se = None;
|
|
|
|
static Cursor xc_s = None;
|
|
|
|
static Cursor xc_sw = None;
|
2020-11-22 12:16:04 +03:00
|
|
|
#if ! HAVE_XCURSOR
|
|
|
|
static Cursor xc_nwse = None;
|
|
|
|
static Cursor xc_nesw = None;
|
|
|
|
static Cursor xc_none = None;
|
|
|
|
#endif // ! HAVE_XCURSOR
|
2014-08-29 15:23:44 +04:00
|
|
|
|
2014-06-16 15:17:57 +04:00
|
|
|
Cursor xc;
|
|
|
|
|
2014-08-29 15:23:44 +04:00
|
|
|
#define cache_cursor(name, var) if (var == None) { \
|
|
|
|
var = XCreateFontCursor(fl_display, name); \
|
|
|
|
} \
|
|
|
|
xc = var
|
|
|
|
|
2014-06-16 15:17:57 +04:00
|
|
|
switch (c) {
|
2014-08-29 15:23:44 +04:00
|
|
|
case FL_CURSOR_ARROW: cache_cursor(XC_left_ptr, xc_arrow); break;
|
|
|
|
case FL_CURSOR_CROSS: cache_cursor(XC_tcross, xc_cross); break;
|
|
|
|
case FL_CURSOR_WAIT: cache_cursor(XC_watch, xc_wait); break;
|
|
|
|
case FL_CURSOR_INSERT: cache_cursor(XC_xterm, xc_insert); break;
|
|
|
|
case FL_CURSOR_HAND: cache_cursor(XC_hand2, xc_hand); break;
|
|
|
|
case FL_CURSOR_HELP: cache_cursor(XC_question_arrow, xc_help); break;
|
|
|
|
case FL_CURSOR_MOVE: cache_cursor(XC_fleur, xc_move); break;
|
|
|
|
case FL_CURSOR_NS: cache_cursor(XC_sb_v_double_arrow, xc_ns); break;
|
|
|
|
case FL_CURSOR_WE: cache_cursor(XC_sb_h_double_arrow, xc_we); break;
|
|
|
|
case FL_CURSOR_NE: cache_cursor(XC_top_right_corner, xc_ne); break;
|
|
|
|
case FL_CURSOR_N: cache_cursor(XC_top_side, xc_n); break;
|
|
|
|
case FL_CURSOR_NW: cache_cursor(XC_top_left_corner, xc_nw); break;
|
|
|
|
case FL_CURSOR_E: cache_cursor(XC_right_side, xc_e); break;
|
|
|
|
case FL_CURSOR_W: cache_cursor(XC_left_side, xc_w); break;
|
|
|
|
case FL_CURSOR_SE: cache_cursor(XC_bottom_right_corner, xc_se); break;
|
|
|
|
case FL_CURSOR_S: cache_cursor(XC_bottom_side, xc_s); break;
|
|
|
|
case FL_CURSOR_SW: cache_cursor(XC_bottom_left_corner, xc_sw); break;
|
2020-11-22 12:16:04 +03:00
|
|
|
#if ! HAVE_XCURSOR
|
|
|
|
case FL_CURSOR_NWSE: cache_pixmap_cursor(c, xc_nwse, pWindow, xc); break;
|
|
|
|
case FL_CURSOR_NESW: cache_pixmap_cursor(c, xc_nesw, pWindow, xc); break;
|
|
|
|
case FL_CURSOR_NONE: cache_pixmap_cursor(c, xc_none, pWindow, xc); break;
|
|
|
|
#endif // ! HAVE_XCURSOR
|
2014-06-16 15:17:57 +04:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-29 15:23:44 +04:00
|
|
|
#undef cache_cursor
|
|
|
|
|
2016-04-18 17:31:07 +03:00
|
|
|
XDefineCursor(fl_display, fl_xid(pWindow), xc);
|
2014-06-16 15:17:57 +04:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-04-18 17:31:07 +03:00
|
|
|
int Fl_X11_Window_Driver::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) {
|
2014-06-16 15:17:57 +04:00
|
|
|
#if ! HAVE_XCURSOR
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
XcursorImage *cursor;
|
|
|
|
Cursor xc;
|
|
|
|
|
|
|
|
if ((hotx < 0) || (hotx >= image->w()))
|
|
|
|
return 0;
|
|
|
|
if ((hoty < 0) || (hoty >= image->h()))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cursor = XcursorImageCreate(image->w(), image->h());
|
|
|
|
if (!cursor)
|
|
|
|
return 0;
|
|
|
|
|
2023-02-13 14:11:04 +03:00
|
|
|
image = (Fl_RGB_Image*)image->copy();
|
2016-10-14 19:35:52 +03:00
|
|
|
const int extra_data = image->ld() ? (image->ld()-image->w()*image->d()) : 0;
|
2014-06-16 15:17:57 +04:00
|
|
|
const uchar *i = (const uchar*)*image->data();
|
|
|
|
XcursorPixel *o = cursor->pixels;
|
|
|
|
for (int y = 0;y < image->h();y++) {
|
|
|
|
for (int x = 0;x < image->w();x++) {
|
2017-02-20 15:40:50 +03:00
|
|
|
uchar r, g, b, a;
|
2014-06-16 15:17:57 +04:00
|
|
|
switch (image->d()) {
|
|
|
|
case 1:
|
2017-02-20 15:40:50 +03:00
|
|
|
r = g = b = i[0];
|
|
|
|
a = 0xff;
|
2014-06-16 15:17:57 +04:00
|
|
|
break;
|
|
|
|
case 2:
|
2017-02-20 15:40:50 +03:00
|
|
|
r = g = b = i[0];
|
|
|
|
a = i[1];
|
2014-06-16 15:17:57 +04:00
|
|
|
break;
|
|
|
|
case 3:
|
2017-02-20 15:40:50 +03:00
|
|
|
r = i[0];
|
|
|
|
g = i[1];
|
|
|
|
b = i[2];
|
|
|
|
a = 0xff;
|
2014-06-16 15:17:57 +04:00
|
|
|
break;
|
|
|
|
case 4:
|
2017-02-20 15:40:50 +03:00
|
|
|
r = i[0];
|
|
|
|
g = i[1];
|
|
|
|
b = i[2];
|
|
|
|
a = i[3];
|
2014-06-16 15:17:57 +04:00
|
|
|
break;
|
2017-02-20 15:40:50 +03:00
|
|
|
default:
|
|
|
|
return 0;
|
2014-06-16 15:17:57 +04:00
|
|
|
}
|
2017-02-20 15:40:50 +03:00
|
|
|
// Alpha needs to be pre-multiplied for X11
|
|
|
|
r = (uchar)((unsigned)r * a / 255);
|
|
|
|
g = (uchar)((unsigned)g * a / 255);
|
|
|
|
b = (uchar)((unsigned)b * a / 255);
|
|
|
|
|
|
|
|
*o = (a<<24) | (r<<16) | (g<<8) | b;
|
2014-06-16 15:17:57 +04:00
|
|
|
i += image->d();
|
|
|
|
o++;
|
|
|
|
}
|
2016-10-14 19:35:52 +03:00
|
|
|
i += extra_data;
|
2014-06-16 15:17:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
cursor->xhot = hotx;
|
|
|
|
cursor->yhot = hoty;
|
|
|
|
|
|
|
|
xc = XcursorImageLoadCursor(fl_display, cursor);
|
2016-04-18 17:31:07 +03:00
|
|
|
XDefineCursor(fl_display, fl_xid(pWindow), xc);
|
2014-06-16 15:17:57 +04:00
|
|
|
XFreeCursor(fl_display, xc);
|
|
|
|
|
|
|
|
XcursorImageDestroy(cursor);
|
2023-02-13 14:11:04 +03:00
|
|
|
delete image;
|
2014-06-16 15:17:57 +04:00
|
|
|
return 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2016-03-21 20:55:10 +03:00
|
|
|
void Fl_X11_Window_Driver::label(const char *name, const char *iname) {
|
2016-03-24 04:20:08 +03:00
|
|
|
if (shown() && !parent()) {
|
1998-10-06 22:21:25 +04:00
|
|
|
if (!name) name = "";
|
2010-12-01 11:13:27 +03:00
|
|
|
int namelen = strlen(name);
|
2002-03-26 00:08:42 +03:00
|
|
|
if (!iname) iname = fl_filename_name(name);
|
2010-12-01 11:13:27 +03:00
|
|
|
int inamelen = strlen(iname);
|
2016-03-21 20:55:10 +03:00
|
|
|
Window win = fl_xid(pWindow);
|
2020-07-01 19:03:10 +03:00
|
|
|
XChangeProperty(fl_display, win, fl_NET_WM_NAME, fl_XaUtf8String, 8, 0, (uchar*)name, namelen); // utf8
|
|
|
|
XChangeProperty(fl_display, win, XA_WM_NAME, XA_STRING, 8, 0, (uchar*)name, namelen); // non-utf8
|
|
|
|
XChangeProperty(fl_display, win, fl_NET_WM_ICON_NAME, fl_XaUtf8String, 8, 0, (uchar*)iname, inamelen); // utf8
|
|
|
|
XChangeProperty(fl_display, win, XA_WM_ICON_NAME, XA_STRING, 8, 0, (uchar*)iname, inamelen); // non-utf8
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Implement the virtual functions for the base Fl_Window class:
|
|
|
|
|
2016-03-22 20:47:44 +03:00
|
|
|
void Fl_X11_Window_Driver::show() {
|
2016-03-24 04:20:08 +03:00
|
|
|
if (!shown()) {
|
1998-10-06 22:21:25 +04:00
|
|
|
fl_open_display();
|
2022-02-22 21:55:11 +03:00
|
|
|
|
|
|
|
#ifdef ENABLE_BOXCHEAT
|
2007-02-02 22:37:30 +03:00
|
|
|
// Don't set background pixel for double-buffered windows...
|
2016-03-22 20:47:44 +03:00
|
|
|
if (pWindow->type() != FL_DOUBLE_WINDOW && can_boxcheat(pWindow->box())) {
|
|
|
|
fl_background_pixel = int(fl_xpixel(pWindow->color()));
|
2007-02-02 22:37:30 +03:00
|
|
|
}
|
2022-02-22 21:55:11 +03:00
|
|
|
#endif // (ENABLE_BOXCHEAT)
|
|
|
|
|
2016-04-19 19:00:30 +03:00
|
|
|
makeWindow();
|
1998-10-06 22:21:25 +04:00
|
|
|
} else {
|
2016-03-22 20:47:44 +03:00
|
|
|
XMapRaised(fl_display, fl_xid(pWindow));
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
}
|