1998-10-20 00:46:58 +04:00
|
|
|
//
|
2005-02-01 06:13:01 +03:00
|
|
|
// "$Id$"
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
|
|
|
// X specific code for the Fast Light Tool Kit (FLTK).
|
|
|
|
//
|
2011-04-24 21:09:41 +04:00
|
|
|
// Copyright 1998-2011 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:
|
|
|
|
//
|
|
|
|
// http://www.fltk.org/COPYING.php
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
2005-04-15 22:00:33 +04:00
|
|
|
// Please report all bugs and problems on the following page:
|
|
|
|
//
|
|
|
|
// http://www.fltk.org/str.php
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
#ifdef WIN32
|
2005-10-13 00:19:30 +04:00
|
|
|
//# include "Fl_win32.cxx"
|
2001-11-27 20:44:08 +03:00
|
|
|
#elif defined(__APPLE__)
|
2005-10-13 00:19:30 +04:00
|
|
|
//# include "Fl_mac.cxx"
|
2008-09-18 23:09:34 +04:00
|
|
|
#elif !defined(FL_DOXYGEN)
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# define CONSOLIDATE_MOTION 1
|
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>
|
|
|
|
# include <FL/x.H>
|
|
|
|
# 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>
|
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"
|
2001-11-27 20:44:08 +03:00
|
|
|
# include <unistd.h>
|
|
|
|
# include <sys/time.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>
|
2011-09-30 18:46:08 +04:00
|
|
|
#ifdef HAVE_XRANDR
|
|
|
|
#include <X11/extensions/Xrandr.h>
|
|
|
|
static int randrEventBase = -1;
|
|
|
|
#endif
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2010-05-27 21:20:18 +04:00
|
|
|
static Fl_Xlib_Graphics_Driver fl_xlib_driver;
|
|
|
|
static Fl_Display_Device fl_xlib_display(&fl_xlib_driver);
|
2010-07-01 17:21:32 +04:00
|
|
|
FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_xlib_driver; // the current target device of graphics operations
|
2011-02-02 15:42:47 +03:00
|
|
|
Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_xlib_display; // the current target surface of graphics operations
|
|
|
|
Fl_Display_Device *Fl_Display_Device::_display = &fl_xlib_display;// the platform display
|
2010-03-14 21:07:24 +03:00
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// interface to poll/select call:
|
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
1999-04-17 05:02:30 +04:00
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# include <poll.h>
|
1999-04-17 05:02:30 +04:00
|
|
|
static pollfd *pollfds = 0;
|
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# else
|
|
|
|
# if HAVE_SYS_SELECT_H
|
|
|
|
# include <sys/select.h>
|
|
|
|
# endif /* HAVE_SYS_SELECT_H */
|
1998-11-08 18:28:42 +03:00
|
|
|
|
1999-04-17 05:02:30 +04:00
|
|
|
// The following #define is only needed for HP-UX 9.x and earlier:
|
2010-06-15 12:20:15 +04:00
|
|
|
//#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
|
1998-11-08 18:28:42 +03:00
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
static fd_set fdsets[3];
|
|
|
|
static int maxfd;
|
2001-11-27 20:44:08 +03:00
|
|
|
# define POLLIN 1
|
|
|
|
# define POLLOUT 4
|
|
|
|
# define POLLERR 8
|
1999-04-17 05:02:30 +04:00
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif /* USE_POLL */
|
1999-04-17 05:02:30 +04:00
|
|
|
|
|
|
|
static int nfds = 0;
|
|
|
|
static int fd_array_size = 0;
|
2001-04-27 18:39:27 +04:00
|
|
|
struct FD {
|
2001-11-27 20:44:08 +03:00
|
|
|
# if !USE_POLL
|
1999-04-17 05:02:30 +04:00
|
|
|
int fd;
|
|
|
|
short events;
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
void (*cb)(int, void*);
|
|
|
|
void* arg;
|
2001-04-27 18:39:27 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static FD *fd = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
|
1999-04-17 05:02:30 +04:00
|
|
|
remove_fd(n,events);
|
|
|
|
int i = nfds++;
|
|
|
|
if (i >= fd_array_size) {
|
2001-04-27 18:39:27 +04:00
|
|
|
FD *temp;
|
1999-04-17 05:02:30 +04:00
|
|
|
fd_array_size = 2*fd_array_size+1;
|
2001-04-27 18:39:27 +04:00
|
|
|
|
|
|
|
if (!fd) temp = (FD*)malloc(fd_array_size*sizeof(FD));
|
|
|
|
else temp = (FD*)realloc(fd, fd_array_size*sizeof(FD));
|
|
|
|
|
|
|
|
if (!temp) return;
|
|
|
|
fd = temp;
|
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2001-04-27 18:39:27 +04:00
|
|
|
pollfd *tpoll;
|
|
|
|
|
|
|
|
if (!pollfds) tpoll = (pollfd*)malloc(fd_array_size*sizeof(pollfd));
|
|
|
|
else tpoll = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
|
|
|
|
|
|
|
|
if (!tpoll) return;
|
|
|
|
pollfds = tpoll;
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1999-04-17 05:02:30 +04:00
|
|
|
}
|
|
|
|
fd[i].cb = cb;
|
|
|
|
fd[i].arg = v;
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2000-06-18 04:38:41 +04:00
|
|
|
pollfds[i].fd = n;
|
|
|
|
pollfds[i].events = events;
|
2001-11-27 20:44:08 +03:00
|
|
|
# else
|
2000-06-18 04:38:41 +04:00
|
|
|
fd[i].fd = n;
|
|
|
|
fd[i].events = events;
|
1998-10-06 22:21:25 +04:00
|
|
|
if (events & POLLIN) FD_SET(n, &fdsets[0]);
|
|
|
|
if (events & POLLOUT) FD_SET(n, &fdsets[1]);
|
|
|
|
if (events & POLLERR) FD_SET(n, &fdsets[2]);
|
|
|
|
if (n > maxfd) maxfd = n;
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
void Fl::add_fd(int n, void (*cb)(int, void*), void* v) {
|
|
|
|
Fl::add_fd(n, POLLIN, cb, v);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
1999-04-17 05:02:30 +04:00
|
|
|
void Fl::remove_fd(int n, int events) {
|
1998-10-06 22:21:25 +04:00
|
|
|
int i,j;
|
2010-06-15 11:49:26 +04:00
|
|
|
# if !USE_POLL
|
2003-05-05 01:45:46 +04:00
|
|
|
maxfd = -1; // recalculate maxfd on the fly
|
2010-06-15 11:49:26 +04:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
for (i=j=0; i<nfds; i++) {
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2000-06-18 04:38:41 +04:00
|
|
|
if (pollfds[i].fd == n) {
|
|
|
|
int e = pollfds[i].events & ~events;
|
|
|
|
if (!e) continue; // if no events left, delete this fd
|
|
|
|
pollfds[j].events = e;
|
|
|
|
}
|
2001-11-27 20:44:08 +03:00
|
|
|
# else
|
1999-04-17 05:02:30 +04:00
|
|
|
if (fd[i].fd == n) {
|
|
|
|
int e = fd[i].events & ~events;
|
|
|
|
if (!e) continue; // if no events left, delete this fd
|
|
|
|
fd[i].events = e;
|
|
|
|
}
|
2010-05-27 21:20:18 +04:00
|
|
|
if (fd[i].fd > maxfd) maxfd = fd[i].fd;
|
2010-06-15 11:49:26 +04:00
|
|
|
# endif
|
1999-04-17 05:02:30 +04:00
|
|
|
// move it down in the array if necessary:
|
|
|
|
if (j<i) {
|
2000-06-18 04:38:41 +04:00
|
|
|
fd[j] = fd[i];
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2000-06-18 04:38:41 +04:00
|
|
|
pollfds[j] = pollfds[i];
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1999-04-17 05:02:30 +04:00
|
|
|
}
|
|
|
|
j++;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
nfds = j;
|
2001-11-27 20:44:08 +03:00
|
|
|
# if !USE_POLL
|
1999-04-17 05:02:30 +04:00
|
|
|
if (events & POLLIN) FD_CLR(n, &fdsets[0]);
|
|
|
|
if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
|
|
|
|
if (events & POLLERR) FD_CLR(n, &fdsets[2]);
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
1999-04-17 05:02:30 +04:00
|
|
|
void Fl::remove_fd(int n) {
|
|
|
|
remove_fd(n, -1);
|
|
|
|
}
|
|
|
|
|
2003-03-09 05:00:06 +03:00
|
|
|
#if 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
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
#if CONSOLIDATE_MOTION
|
2002-04-10 05:32:03 +04:00
|
|
|
else if (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
|
|
|
// these pointers are set by the Fl::lock() function:
|
|
|
|
static void nothing() {}
|
|
|
|
void (*fl_lock_function)() = nothing;
|
|
|
|
void (*fl_unlock_function)() = nothing;
|
|
|
|
|
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.
|
|
|
|
int fl_wait(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;}
|
1998-10-06 22:21:25 +04:00
|
|
|
|
2001-11-27 20:44:08 +03:00
|
|
|
# if !USE_POLL
|
1998-10-06 22:21:25 +04:00
|
|
|
fd_set fdt[3];
|
|
|
|
fdt[0] = fdsets[0];
|
|
|
|
fdt[1] = fdsets[1];
|
|
|
|
fdt[2] = fdsets[2];
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
int n;
|
|
|
|
|
2001-12-07 01:16:49 +03:00
|
|
|
fl_unlock_function();
|
|
|
|
|
2000-06-18 04:38:41 +04:00
|
|
|
if (time_to_wait < 2147483.648) {
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2000-06-18 04:38:41 +04:00
|
|
|
n = ::poll(pollfds, nfds, int(time_to_wait*1000 + .5));
|
2001-11-27 20:44:08 +03:00
|
|
|
# else
|
2000-06-18 04:38:41 +04:00
|
|
|
timeval t;
|
|
|
|
t.tv_sec = int(time_to_wait);
|
|
|
|
t.tv_usec = int(1000000 * (time_to_wait-t.tv_sec));
|
|
|
|
n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
} else {
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2000-06-18 04:38:41 +04:00
|
|
|
n = ::poll(pollfds, nfds, -1);
|
2001-11-27 20:44:08 +03:00
|
|
|
# else
|
2000-06-18 04:38:41 +04:00
|
|
|
n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2001-12-07 01:16:49 +03:00
|
|
|
|
|
|
|
fl_lock_function();
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
if (n > 0) {
|
|
|
|
for (int i=0; i<nfds; i++) {
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2000-06-18 04:38:41 +04:00
|
|
|
if (pollfds[i].revents) fd[i].cb(pollfds[i].fd, fd[i].arg);
|
2001-11-27 20:44:08 +03:00
|
|
|
# else
|
1999-04-17 05:02:30 +04:00
|
|
|
int f = fd[i].fd;
|
1998-10-06 22:21:25 +04:00
|
|
|
short revents = 0;
|
|
|
|
if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
|
|
|
|
if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
|
|
|
|
if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
|
1999-04-17 05:02:30 +04:00
|
|
|
if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
}
|
2000-06-18 04:38:41 +04:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
|
|
|
|
int fl_ready() {
|
|
|
|
if (XQLength(fl_display)) return 1;
|
2008-09-02 16:03:30 +04:00
|
|
|
if (!nfds) return 0; // nothing to select or poll
|
2001-11-27 20:44:08 +03:00
|
|
|
# if USE_POLL
|
2000-06-18 04:38:41 +04:00
|
|
|
return ::poll(pollfds, nfds, 0);
|
2001-11-27 20:44:08 +03:00
|
|
|
# else
|
2000-06-18 04:38:41 +04:00
|
|
|
timeval t;
|
|
|
|
t.tv_sec = 0;
|
|
|
|
t.tv_usec = 0;
|
|
|
|
fd_set fdt[3];
|
|
|
|
fdt[0] = fdsets[0];
|
|
|
|
fdt[1] = fdsets[1];
|
|
|
|
fdt[2] = fdsets[2];
|
|
|
|
return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
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;
|
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
|
|
|
XIM fl_xim_im = 0;
|
|
|
|
XIC fl_xim_ic = 0;
|
|
|
|
char fl_is_over_the_spot = 0;
|
|
|
|
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;
|
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;
|
|
|
|
//Atom fl_XdndProxy;
|
2006-01-04 22:53:34 +03:00
|
|
|
Atom fl_XdndURIList;
|
2010-11-30 19:36:38 +03:00
|
|
|
Atom fl_Xatextplainutf;
|
|
|
|
Atom fl_Xatextplain;
|
|
|
|
static Atom fl_XaText;
|
|
|
|
Atom fl_XaCompoundText;
|
2008-09-11 03:56:49 +04:00
|
|
|
Atom fl_XaUtf8String;
|
|
|
|
Atom fl_XaTextUriList;
|
2010-12-01 11:13:27 +03:00
|
|
|
Atom fl_NET_WM_NAME; // utf8 aware window label
|
|
|
|
Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name
|
1998-10-06 22:21:25 +04:00
|
|
|
|
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];
|
|
|
|
sprintf(buf1, "XRequest.%d", e->request_code);
|
|
|
|
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);
|
|
|
|
|
|
|
|
void fl_new_ic()
|
|
|
|
{
|
2009-03-07 18:15:29 +03:00
|
|
|
XVaNestedList preedit_attr = NULL;
|
|
|
|
XVaNestedList status_attr = NULL;
|
|
|
|
static XFontSet fs = NULL;
|
|
|
|
char *fnt;
|
|
|
|
char **missing_list;
|
|
|
|
int missing_count;
|
|
|
|
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
|
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);
|
|
|
|
|
|
|
|
if (!XGetIMValues(fl_xim_im, XNQueryInputStyle,
|
|
|
|
&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) {
|
|
|
|
fl_xim_ic = XCreateIC(fl_xim_im,
|
|
|
|
XNInputStyle, (XIMPreeditPosition | XIMStatusArea),
|
|
|
|
XNPreeditAttributes, preedit_attr,
|
|
|
|
XNStatusAttributes, status_attr,
|
|
|
|
NULL);
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
if (!fl_xim_ic && predit) {
|
|
|
|
fl_xim_ic = XCreateIC(fl_xim_im,
|
|
|
|
XNInputStyle, (XIMPreeditPosition | XIMStatusNothing),
|
|
|
|
XNPreeditAttributes, preedit_attr,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
XFree(preedit_attr);
|
|
|
|
XFree(status_attr);
|
|
|
|
if (!fl_xim_ic) {
|
|
|
|
fl_is_over_the_spot = 0;
|
|
|
|
fl_xim_ic = XCreateIC(fl_xim_im,
|
|
|
|
XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
|
|
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
fl_is_over_the_spot = 1;
|
|
|
|
XVaNestedList status_attr = NULL;
|
|
|
|
status_attr = XVaCreateNestedList(0, XNAreaNeeded, &status_area, NULL);
|
|
|
|
|
|
|
|
XGetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
|
|
|
|
XFree(status_attr);
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static XRectangle spot;
|
|
|
|
static int spotf = -1;
|
|
|
|
static int spots = -1;
|
|
|
|
|
|
|
|
void fl_reset_spot(void)
|
|
|
|
{
|
2009-03-07 18:15:29 +03:00
|
|
|
spot.x = -1;
|
|
|
|
spot.y = -1;
|
|
|
|
//if (fl_xim_ic) XUnsetICFocus(fl_xim_ic);
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
|
2008-12-27 22:22:30 +03:00
|
|
|
void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
|
2008-09-11 03:56:49 +04:00
|
|
|
{
|
2009-03-07 18:15:29 +03:00
|
|
|
int change = 0;
|
|
|
|
XVaNestedList preedit_attr;
|
|
|
|
static XFontSet fs = NULL;
|
|
|
|
char **missing_list;
|
|
|
|
int missing_count;
|
|
|
|
char *def_string;
|
|
|
|
char *fnt = NULL;
|
|
|
|
bool must_free_fnt =true;
|
|
|
|
|
|
|
|
static XIC ic = NULL;
|
|
|
|
|
|
|
|
if (!fl_xim_ic || !fl_is_over_the_spot) return;
|
|
|
|
//XSetICFocus(fl_xim_ic);
|
|
|
|
if (X != spot.x || Y != spot.y) {
|
|
|
|
spot.x = X;
|
|
|
|
spot.y = Y;
|
|
|
|
spot.height = H;
|
|
|
|
spot.width = W;
|
|
|
|
change = 1;
|
|
|
|
}
|
|
|
|
if (font != spotf || size != spots) {
|
|
|
|
spotf = font;
|
|
|
|
spots = size;
|
|
|
|
change = 1;
|
|
|
|
if (fs) {
|
|
|
|
XFreeFontSet(fl_display, fs);
|
|
|
|
}
|
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
|
|
|
fnt = NULL; // fl_get_font_xfld(font, size);
|
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);
|
2008-09-11 03:56:49 +04:00
|
|
|
#else
|
2009-03-07 18:15:29 +03:00
|
|
|
fnt = fl_get_font_xfld(font, size);
|
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);
|
2008-09-11 03:56:49 +04:00
|
|
|
#endif
|
2009-03-07 18:15:29 +03:00
|
|
|
}
|
|
|
|
if (fl_xim_ic != ic) {
|
|
|
|
ic = fl_xim_ic;
|
|
|
|
change = 1;
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
if (fnt && must_free_fnt) free(fnt);
|
|
|
|
if (!change) return;
|
2008-09-11 03:56:49 +04:00
|
|
|
|
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
preedit_attr = XVaCreateNestedList(0,
|
|
|
|
XNSpotLocation, &spot,
|
|
|
|
XNFontSet, fs, NULL);
|
|
|
|
XSetICValues(fl_xim_ic, XNPreeditAttributes, preedit_attr, NULL);
|
|
|
|
XFree(preedit_attr);
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void fl_set_status(int x, int y, int w, int h)
|
|
|
|
{
|
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;
|
|
|
|
if (!fl_xim_ic) return;
|
|
|
|
status_attr = XVaCreateNestedList(0, XNArea, &status_area, NULL);
|
|
|
|
|
|
|
|
XSetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
|
|
|
|
XFree(status_attr);
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
|
2011-02-26 17:14:50 +03:00
|
|
|
void fl_init_xim() {
|
|
|
|
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;
|
|
|
|
if (fl_xim_im) return;
|
|
|
|
|
|
|
|
fl_xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
|
|
|
|
xim_styles = NULL;
|
|
|
|
fl_xim_ic = NULL;
|
|
|
|
|
|
|
|
if (fl_xim_im) {
|
|
|
|
XGetIMValues (fl_xim_im, XNQueryInputStyle,
|
|
|
|
&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) {
|
|
|
|
fl_new_ic();
|
|
|
|
} else {
|
2011-02-26 17:14:50 +03:00
|
|
|
if (xim_warning)
|
|
|
|
Fl::warning("No XIM style found");
|
2009-03-07 18:15:29 +03:00
|
|
|
XCloseIM(fl_xim_im);
|
|
|
|
fl_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;
|
|
|
|
}
|
|
|
|
if (!fl_xim_ic) {
|
2011-02-26 17:14:50 +03:00
|
|
|
if (xim_warning)
|
|
|
|
Fl::warning("XCreateIC() failed");
|
2009-03-07 18:15:29 +03:00
|
|
|
XCloseIM(fl_xim_im);
|
|
|
|
fl_xim_im = NULL;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
void fl_open_display() {
|
|
|
|
if (fl_display) return;
|
|
|
|
|
2008-09-11 03:56:49 +04:00
|
|
|
setlocale(LC_CTYPE, "");
|
|
|
|
XSetLocaleModifiers("");
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
XSetIOErrorHandler(io_error_handler);
|
|
|
|
XSetErrorHandler(xerror_handler);
|
|
|
|
|
|
|
|
Display *d = XOpenDisplay(0);
|
|
|
|
if (!d) Fl::fatal("Can't open display: %s",XDisplayName(0));
|
|
|
|
|
2000-02-23 12:23:53 +03:00
|
|
|
fl_open_display(d);
|
|
|
|
}
|
|
|
|
|
2011-09-30 18:46:08 +04:00
|
|
|
|
2000-02-23 12:23:53 +03:00
|
|
|
void fl_open_display(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);
|
|
|
|
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_XdndProxy = XInternAtom(d, "XdndProxy", 0);
|
|
|
|
fl_XdndEnter = XInternAtom(d, "XdndEnter", 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);
|
|
|
|
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);
|
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);
|
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);
|
2008-09-11 03:56:49 +04:00
|
|
|
fl_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
|
2011-09-30 18:46:08 +04:00
|
|
|
#ifdef HAVE_XRANDR
|
|
|
|
int error_base;
|
|
|
|
if (XRRQueryExtension(d, &randrEventBase, &error_base))
|
|
|
|
XRRSelectInput(d, RootWindow(d, fl_screen), RRScreenChangeNotifyMask);
|
|
|
|
else randrEventBase = -1;
|
|
|
|
#endif
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void fl_close_display() {
|
|
|
|
Fl::remove_fd(ConnectionNumber(fl_display));
|
|
|
|
XCloseDisplay(fl_display);
|
|
|
|
}
|
|
|
|
|
2006-10-30 17:16:08 +03:00
|
|
|
static int fl_workarea_xywh[4] = { -1, -1, -1, -1 };
|
|
|
|
|
|
|
|
static void fl_init_workarea() {
|
1998-10-06 22:21:25 +04:00
|
|
|
fl_open_display();
|
2006-10-30 17:16:08 +03:00
|
|
|
|
|
|
|
Atom _NET_WORKAREA = XInternAtom(fl_display, "_NET_WORKAREA", 0);
|
|
|
|
Atom actual;
|
|
|
|
unsigned long count, remaining;
|
|
|
|
int format;
|
|
|
|
unsigned *xywh;
|
|
|
|
|
Fix STR#2695 & 2697: correct computation of work areas with multiple screens.
This introduces 3 new functions
static void Fl::screen_work_area(X,Y,W,H)
static void Fl::screen_work_area(X,Y,W,H,mx,my)
static void Fl::screen_work_area(X,Y,W,H,screen_no)
that compute screen work areas and are used by FLTK to position menu windows.
The Fl::x(),y(),w(),h() functions are made consistent across platforms: they return
the origin/size of the work area of the main screen (as far as possible, see below).
On the Mac OS platform, all screen functions reflect changes in screen number and
positions without requiring the application to restart.
On the X11 platform, I did not find an API to compute the main screen work area
in all conditions. What's used does compute the correct work area when there's
a single screen, but not when there are several, because it returns an area that
encompasses all screens. The implemented workaround is that Fl::x(),y(),w(),h()
and Fl::screen_work_area(X,Y,W,H,0) return the exact work area when there's
a single screen, and return the full screen area when there are several.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9084 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
2011-09-29 20:04:24 +04:00
|
|
|
/* If there are several screens, the _NET_WORKAREA property
|
|
|
|
does not give the work area of the main screen, but that of all screens together.
|
|
|
|
Therefore, we use this property only when there is a single screen,
|
|
|
|
and fall back to the main screen full area when there are several screens.
|
|
|
|
*/
|
|
|
|
if (Fl::screen_count() > 1 || XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
|
2006-10-30 17:16:08 +03:00
|
|
|
_NET_WORKAREA, 0, 4 * sizeof(unsigned), False,
|
2009-03-07 18:15:29 +03:00
|
|
|
XA_CARDINAL, &actual, &format, &count, &remaining,
|
|
|
|
(unsigned char **)&xywh) || !xywh || !xywh[2] ||
|
|
|
|
!xywh[3])
|
2006-10-30 17:16:08 +03:00
|
|
|
{
|
Fix STR#2695 & 2697: correct computation of work areas with multiple screens.
This introduces 3 new functions
static void Fl::screen_work_area(X,Y,W,H)
static void Fl::screen_work_area(X,Y,W,H,mx,my)
static void Fl::screen_work_area(X,Y,W,H,screen_no)
that compute screen work areas and are used by FLTK to position menu windows.
The Fl::x(),y(),w(),h() functions are made consistent across platforms: they return
the origin/size of the work area of the main screen (as far as possible, see below).
On the Mac OS platform, all screen functions reflect changes in screen number and
positions without requiring the application to restart.
On the X11 platform, I did not find an API to compute the main screen work area
in all conditions. What's used does compute the correct work area when there's
a single screen, but not when there are several, because it returns an area that
encompasses all screens. The implemented workaround is that Fl::x(),y(),w(),h()
and Fl::screen_work_area(X,Y,W,H,0) return the exact work area when there's
a single screen, and return the full screen area when there are several.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9084 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
2011-09-29 20:04:24 +04:00
|
|
|
Fl::screen_xywh(fl_workarea_xywh[0],
|
|
|
|
fl_workarea_xywh[1],
|
|
|
|
fl_workarea_xywh[2],
|
|
|
|
fl_workarea_xywh[3], 0);
|
2006-10-30 17:16:08 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fl_workarea_xywh[0] = (int)xywh[0];
|
|
|
|
fl_workarea_xywh[1] = (int)xywh[1];
|
|
|
|
fl_workarea_xywh[2] = (int)xywh[2];
|
|
|
|
fl_workarea_xywh[3] = (int)xywh[3];
|
|
|
|
XFree(xywh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Fl::x() {
|
|
|
|
if (fl_workarea_xywh[0] < 0) fl_init_workarea();
|
|
|
|
return fl_workarea_xywh[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
int Fl::y() {
|
|
|
|
if (fl_workarea_xywh[0] < 0) fl_init_workarea();
|
|
|
|
return fl_workarea_xywh[1];
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int Fl::w() {
|
2006-10-30 17:16:08 +03:00
|
|
|
if (fl_workarea_xywh[0] < 0) fl_init_workarea();
|
|
|
|
return fl_workarea_xywh[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
int Fl::h() {
|
|
|
|
if (fl_workarea_xywh[0] < 0) fl_init_workarea();
|
|
|
|
return fl_workarea_xywh[3];
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2002-08-09 07:17:30 +04:00
|
|
|
void Fl::get_mouse(int &xx, int &yy) {
|
1998-10-06 22:21:25 +04:00
|
|
|
fl_open_display();
|
|
|
|
Window root = RootWindow(fl_display, fl_screen);
|
|
|
|
Window c; int mx,my,cx,cy; unsigned int mask;
|
|
|
|
XQueryPointer(fl_display,root,&root,&c,&mx,&my,&cx,&cy,&mask);
|
2002-08-09 07:17:30 +04:00
|
|
|
xx = mx;
|
|
|
|
yy = my;
|
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:
|
|
|
|
|
|
|
|
Fl_Widget *fl_selection_requestor;
|
2002-03-26 20:37:42 +03:00
|
|
|
char *fl_selection_buffer[2];
|
|
|
|
int fl_selection_length[2];
|
|
|
|
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
|
|
|
|
|
|
|
// Call this when a "paste" operation happens:
|
|
|
|
void Fl::paste(Fl_Widget &receiver, int clipboard) {
|
|
|
|
if (fl_i_own_selection[clipboard]) {
|
|
|
|
// We already have it, do it quickly without window server.
|
|
|
|
// Notice that the text is clobbered if set_selection is
|
|
|
|
// called in response to FL_PASTE!
|
2002-03-26 20:37:42 +03:00
|
|
|
Fl::e_text = fl_selection_buffer[clipboard];
|
|
|
|
Fl::e_length = fl_selection_length[clipboard];
|
2003-06-15 08:27:35 +04:00
|
|
|
if (!Fl::e_text) Fl::e_text = (char *)"";
|
2002-03-07 22:22:58 +03:00
|
|
|
receiver.handle(FL_PASTE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// otherwise get the window server to return it:
|
|
|
|
fl_selection_requestor = &receiver;
|
|
|
|
Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
|
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
|
|
|
}
|
|
|
|
|
|
|
|
Window fl_dnd_source_window;
|
|
|
|
Atom *fl_dnd_source_types; // null-terminated list of data types being supplied
|
|
|
|
Atom fl_dnd_type;
|
|
|
|
Atom fl_dnd_source_action;
|
|
|
|
Atom fl_dnd_action;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Code for copying to clipboard and DnD out of the program:
|
|
|
|
|
|
|
|
void Fl::copy(const char *stuff, int len, int clipboard) {
|
|
|
|
if (!stuff || len<0) return;
|
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;
|
|
|
|
Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
|
|
|
|
XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
1999-01-05 20:53:00 +03:00
|
|
|
static void set_event_xy() {
|
2001-11-27 20:44:08 +03:00
|
|
|
# if CONSOLIDATE_MOTION
|
1998-12-07 16:34:27 +03:00
|
|
|
send_motion = 0;
|
2001-11-27 20:44:08 +03:00
|
|
|
# endif
|
2002-05-23 20:47:41 +04:00
|
|
|
Fl::e_x_root = fl_xevent->xbutton.x_root;
|
|
|
|
Fl::e_x = fl_xevent->xbutton.x;
|
|
|
|
Fl::e_y_root = fl_xevent->xbutton.y_root;
|
|
|
|
Fl::e_y = fl_xevent->xbutton.y;
|
|
|
|
Fl::e_state = fl_xevent->xbutton.state << 16;
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
2008-09-11 03:56:49 +04:00
|
|
|
static Window xim_win = 0;
|
|
|
|
|
|
|
|
if (fl_xim_ic && xevent.type == DestroyNotify &&
|
2009-03-07 18:15:29 +03:00
|
|
|
xid != xim_win && !fl_find(xid))
|
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 */
|
|
|
|
XSetLocaleModifiers("@im=");
|
|
|
|
fl_xim_im = NULL;
|
|
|
|
fl_init_xim();
|
|
|
|
} else {
|
2009-04-17 02:23:36 +04:00
|
|
|
XCloseIM(xim_im); // see STR 2185 for comment
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fl_xim_ic && (xevent.type == FocusIn))
|
|
|
|
{
|
|
|
|
#define POOR_XIM
|
|
|
|
#ifdef POOR_XIM
|
|
|
|
if (xim_win != xid)
|
2009-03-07 18:15:29 +03:00
|
|
|
{
|
|
|
|
xim_win = xid;
|
|
|
|
XDestroyIC(fl_xim_ic);
|
|
|
|
fl_xim_ic = NULL;
|
|
|
|
fl_new_ic();
|
|
|
|
XSetICValues(fl_xim_ic,
|
|
|
|
XNFocusWindow, xevent.xclient.window,
|
|
|
|
XNClientWindow, xid,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
|
2008-09-11 03:56:49 +04:00
|
|
|
#else
|
|
|
|
if (Fl::first_window() && Fl::first_window()->modal()) {
|
|
|
|
Window x = fl_xid(Fl::first_window());
|
|
|
|
if (x != xim_win) {
|
2009-03-07 18:15:29 +03:00
|
|
|
xim_win = x;
|
2008-09-11 03:56:49 +04:00
|
|
|
XSetICValues(fl_xim_ic,
|
|
|
|
XNFocusWindow, xim_win,
|
|
|
|
XNClientWindow, xim_win,
|
|
|
|
NULL);
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
|
|
|
} else if (xim_win != xid && xid) {
|
|
|
|
xim_win = xid;
|
|
|
|
XSetICValues(fl_xim_ic,
|
|
|
|
XNFocusWindow, xevent.xclient.window,
|
|
|
|
XNClientWindow, xid,
|
|
|
|
//XNFocusWindow, xim_win,
|
|
|
|
//XNClientWindow, xim_win,
|
|
|
|
NULL);
|
|
|
|
fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-11-17 00:15:27 +03:00
|
|
|
if ( XFilterEvent((XEvent *)&xevent, 0) )
|
|
|
|
return(1);
|
2011-09-30 18:46:08 +04:00
|
|
|
|
|
|
|
#ifdef HAVE_XRANDR
|
|
|
|
if( randrEventBase >= 0 && xevent.type == randrEventBase + RRScreenChangeNotify) {
|
|
|
|
XRRUpdateConfiguration (&xevent);
|
|
|
|
Fl::call_screen_init();
|
|
|
|
fl_init_workarea();
|
|
|
|
Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
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: {
|
|
|
|
if (!fl_selection_requestor) return 0;
|
2008-09-11 03:56:49 +04:00
|
|
|
static unsigned char* buffer = 0;
|
2002-03-07 22:22:58 +03:00
|
|
|
if (buffer) {XFree(buffer); 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,
|
|
|
|
bytesread/4, 65536, 1, 0,
|
|
|
|
&actual, &format, &count, &remaining,
|
|
|
|
&portion)) break; // quit on error
|
2010-11-30 19:36:38 +03:00
|
|
|
if (actual == TARGETS || actual == XA_ATOM) {
|
|
|
|
Atom type = XA_STRING;
|
|
|
|
for (unsigned i = 0; i<count; i++) {
|
|
|
|
Atom t = ((Atom*)portion)[i];
|
|
|
|
if (t == fl_Xatextplainutf ||
|
|
|
|
t == fl_Xatextplain ||
|
|
|
|
t == fl_XaUtf8String) {type = t; break;}
|
|
|
|
// rest are only used if no utf-8 available:
|
2011-01-16 01:47:30 +03:00
|
|
|
if (t == fl_XaText ||
|
|
|
|
t == fl_XaTextUriList ||
|
2010-11-30 19:36:38 +03:00
|
|
|
t == fl_XaCompoundText) type = t;
|
2011-01-16 01:47:30 +03:00
|
|
|
}
|
2010-11-30 19:36:38 +03:00
|
|
|
XFree(portion);
|
|
|
|
Atom property = xevent.xselection.property;
|
|
|
|
XConvertSelection(fl_display, property, type, property,
|
|
|
|
fl_xid(Fl::first_window()),
|
|
|
|
fl_event_time);
|
|
|
|
return true;
|
2002-03-07 22:22:58 +03:00
|
|
|
}
|
2011-05-30 20:47:48 +04:00
|
|
|
// Make sure we got something sane...
|
|
|
|
if ((portion == NULL) || (format != 8) || (count == 0)) {
|
|
|
|
if (portion) XFree(portion);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
buffer = (unsigned char*)realloc(buffer, bytesread+count+remaining+1);
|
|
|
|
memcpy(buffer+bytesread, portion, count);
|
2011-01-16 01:47:30 +03:00
|
|
|
XFree(portion);
|
2011-05-30 20:47:48 +04:00
|
|
|
bytesread += count;
|
|
|
|
// Cannot trust data to be null terminated
|
|
|
|
buffer[bytesread] = '\0';
|
2002-03-07 22:22:58 +03:00
|
|
|
if (!remaining) break;
|
|
|
|
}
|
2010-12-10 15:05:01 +03:00
|
|
|
if (buffer) {
|
|
|
|
buffer[bytesread] = 0;
|
2010-12-11 17:59:22 +03:00
|
|
|
convert_crlf(buffer, bytesread);
|
2010-12-10 15:05:01 +03:00
|
|
|
}
|
2003-06-15 08:27:35 +04:00
|
|
|
Fl::e_text = buffer ? (char*)buffer : (char *)"";
|
2002-08-09 07:17:30 +04:00
|
|
|
Fl::e_length = bytesread;
|
2004-12-03 06:14:17 +03:00
|
|
|
int old_event = Fl::e_number;
|
|
|
|
fl_selection_requestor->handle(Fl::e_number = FL_PASTE);
|
|
|
|
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
|
|
|
|
// XA_SECONDARY for that) and send an XdndFinished message. It is not
|
|
|
|
// clear if this has to be delayed until now or if it can be done
|
|
|
|
// immediatly after calling XConvertSelection.
|
|
|
|
if (fl_xevent->xselection.property == XA_SECONDARY &&
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_dnd_source_window) {
|
2002-03-07 22:22:58 +03:00
|
|
|
fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished,
|
|
|
|
fl_xevent->xselection.requestor);
|
|
|
|
fl_dnd_source_window = 0; // don't send a second time
|
|
|
|
}
|
|
|
|
return 1;}
|
|
|
|
|
|
|
|
case SelectionClear: {
|
|
|
|
int clipboard = fl_xevent->xselectionclear.selection == CLIPBOARD;
|
|
|
|
fl_i_own_selection[clipboard] = 0;
|
|
|
|
return 1;}
|
|
|
|
|
|
|
|
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;
|
|
|
|
if (e.target == TARGETS) {
|
2010-11-30 19:36:38 +03:00
|
|
|
Atom a[3] = {fl_XaUtf8String, XA_STRING, fl_XaText};
|
2002-03-07 22:22:58 +03:00
|
|
|
XChangeProperty(fl_display, e.requestor, e.property,
|
2010-11-30 19:36:38 +03:00
|
|
|
XA_ATOM, atom_bits, 0, (unsigned char*)a, 3);
|
2002-03-26 20:37:42 +03:00
|
|
|
} else if (/*e.target == XA_STRING &&*/ fl_selection_length[clipboard]) {
|
2010-11-30 19:36:38 +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) {
|
|
|
|
// 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]);
|
|
|
|
}
|
2002-03-07 22:22:58 +03:00
|
|
|
} else {
|
|
|
|
// char* x = XGetAtomName(fl_display,e.target);
|
|
|
|
// fprintf(stderr,"selection request of %s\n",x);
|
|
|
|
// XFree(x);
|
|
|
|
e.property = 0;
|
|
|
|
}
|
|
|
|
XSendEvent(fl_display, e.requestor, 0, 0, (XEvent *)&e);}
|
|
|
|
return 1;
|
|
|
|
|
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;
|
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);
|
|
|
|
Fl_X* X = Fl_X::i(window);
|
|
|
|
if (X) { // indicates the FLTK window was not closed
|
2011-05-23 23:40:23 +04:00
|
|
|
X->xid = (Window)0; // indicates the X11 window was already destroyed
|
2011-05-23 20:49:02 +04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
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) {
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
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
|
2005-02-01 06:13:01 +03:00
|
|
|
// printf("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;
|
|
|
|
unsigned char *buffer = 0;
|
|
|
|
XGetWindowProperty(fl_display, fl_dnd_source_window, fl_XdndTypeList,
|
|
|
|
0, 0x8000000L, False, XA_ATOM, &actual, &format,
|
|
|
|
&count, &remaining, &buffer);
|
|
|
|
if (actual != XA_ATOM || format != 32 || count<4 || !buffer)
|
|
|
|
goto FAILED;
|
|
|
|
delete [] fl_dnd_source_types;
|
|
|
|
fl_dnd_source_types = new Atom[count+1];
|
|
|
|
for (unsigned i = 0; i < count; i++) {
|
|
|
|
fl_dnd_source_types[i] = ((Atom*)buffer)[i];
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
2009-03-07 18:15:29 +03:00
|
|
|
fl_dnd_source_types[count] = 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
|
|
|
|
|
|
|
// Loop through the source types and pick the first text type...
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; fl_dnd_source_types[i]; i ++)
|
|
|
|
{
|
|
|
|
// printf("fl_dnd_source_types[%d] = %ld (%s)\n", i,
|
2009-03-07 18:15:29 +03:00
|
|
|
// fl_dnd_source_types[i],
|
|
|
|
// XGetAtomName(fl_display, fl_dnd_source_types[i]));
|
2005-02-01 06:13:01 +03:00
|
|
|
|
|
|
|
if (!strncmp(XGetAtomName(fl_display, fl_dnd_source_types[i]),
|
2009-03-07 18:15:29 +03:00
|
|
|
"text/", 5))
|
|
|
|
break;
|
2005-02-01 06:13:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fl_dnd_source_types[i])
|
|
|
|
fl_dnd_type = fl_dnd_source_types[i];
|
|
|
|
else
|
|
|
|
fl_dnd_type = fl_dnd_source_types[0];
|
|
|
|
|
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) {
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
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::e_x_root = data[2]>>16;
|
|
|
|
Fl::e_y_root = data[2]&0xFFFF;
|
|
|
|
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) {
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = window;
|
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();
|
|
|
|
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
|
|
|
|
|
|
|
}
|
|
|
|
break;}
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
case UnmapNotify:
|
|
|
|
event = FL_HIDE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Expose:
|
1998-10-20 01:00:26 +04:00
|
|
|
Fl_X::i(window)->wait_for_expose = 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:
|
1998-10-19 21:53:09 +04:00
|
|
|
window->damage(FL_DAMAGE_EXPOSE, xevent.xexpose.x, xevent.xexpose.y,
|
2009-03-07 18:15:29 +03:00
|
|
|
xevent.xexpose.width, xevent.xexpose.height);
|
1998-10-06 22:21:25 +04:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
case FocusIn:
|
2008-09-11 03:56:49 +04:00
|
|
|
if (fl_xim_ic) XSetICFocus(fl_xim_ic);
|
1998-10-06 22:21:25 +04:00
|
|
|
event = FL_FOCUS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FocusOut:
|
2008-09-11 03:56:49 +04:00
|
|
|
if (fl_xim_ic) XUnsetICFocus(fl_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));
|
2008-09-11 03:56:49 +04:00
|
|
|
static char *buffer = NULL;
|
|
|
|
static int buffer_len = 0;
|
2001-10-27 07:45:29 +04:00
|
|
|
int len;
|
1998-10-06 22:21:25 +04:00
|
|
|
KeySym keysym;
|
2008-09-11 03:56:49 +04:00
|
|
|
if (buffer_len == 0) {
|
2009-03-07 18:15:29 +03:00
|
|
|
buffer_len = 4096;
|
|
|
|
buffer = (char*) malloc(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
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
if (fl_xim_ic) {
|
2010-11-17 00:15:27 +03:00
|
|
|
Status status;
|
|
|
|
len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
|
|
|
|
buffer, buffer_len, &keysym, &status);
|
|
|
|
|
|
|
|
while (status == XBufferOverflow && buffer_len < 50000) {
|
|
|
|
buffer_len = buffer_len * 5 + 1;
|
|
|
|
buffer = (char*)realloc(buffer, buffer_len);
|
|
|
|
len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
|
|
|
|
buffer, buffer_len, &keysym, &status);
|
|
|
|
}
|
|
|
|
keysym = XKeycodeToKeysym(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),
|
2008-09-11 03:56:49 +04:00
|
|
|
buffer, 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):
|
|
|
|
// if (!len) {buffer[0] = char(keysym); len = 1;}
|
|
|
|
len = fl_utf8encode(XKeysymToUcs(keysym), buffer);
|
|
|
|
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:
|
2010-11-17 00:15:27 +03:00
|
|
|
keysym = XKeycodeToKeysym(fl_display, keycode, 0);
|
|
|
|
}
|
2008-09-11 03:56:49 +04:00
|
|
|
}
|
2004-03-01 01:16:11 +03:00
|
|
|
// MRS: Can't use Fl::event_state(FL_CTRL) since the state is not
|
|
|
|
// set until set_event_xy() is called later...
|
|
|
|
if ((xevent.xkey.state & ControlMask) && keysym == '-') buffer[0] = 0x1f; // ^_
|
2001-10-27 07:45:29 +04:00
|
|
|
buffer[len] = 0;
|
|
|
|
Fl::e_text = buffer;
|
|
|
|
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
|
|
|
|
2009-12-13 15:03:26 +03:00
|
|
|
// Bool XkbSetDetectableAutorepeat ( display, detectable, supported_rtrn )
|
|
|
|
// Display * display ;
|
|
|
|
// Bool detectable ;
|
|
|
|
// Bool * supported_rtrn ;
|
2011-01-16 01:47:30 +03:00
|
|
|
// ...would be the easy way to corrct this isuue. Unfortunatly, 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
|
2009-12-13 15:03:26 +03:00
|
|
|
// neccessary condition is an identical key event pending right after
|
|
|
|
// the bogus KeyUp.
|
|
|
|
// The new code introduced Dec 2009 differs in that it only check the very
|
|
|
|
// 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:
|
1999-04-23 10:55:53 +04:00
|
|
|
keysym = XKeycodeToKeysym(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
|
|
|
|
# if 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) {
|
|
|
|
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-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:
|
|
|
|
keysym = FL_Volume_Down;
|
|
|
|
break;
|
|
|
|
case 0x1008FF12: // XF86XK_AudioMute:
|
|
|
|
keysym = FL_Volume_Mute;
|
|
|
|
break;
|
|
|
|
case 0x1008FF13: // XF86XK_AudioRaiseVolume:
|
|
|
|
keysym = FL_Volume_Up;
|
|
|
|
break;
|
|
|
|
case 0x1008FF14: // XF86XK_AudioPlay:
|
|
|
|
keysym = FL_Media_Play;
|
|
|
|
break;
|
|
|
|
case 0x1008FF15: // XF86XK_AudioStop:
|
|
|
|
keysym = FL_Media_Stop;
|
|
|
|
break;
|
|
|
|
case 0x1008FF16: // XF86XK_AudioPrev:
|
|
|
|
keysym = FL_Media_Prev;
|
|
|
|
break;
|
|
|
|
case 0x1008FF17: // XF86XK_AudioNext:
|
|
|
|
keysym = FL_Media_Next;
|
|
|
|
break;
|
|
|
|
case 0x1008FF18: // XF86XK_HomePage:
|
|
|
|
keysym = FL_Home_Page;
|
|
|
|
break;
|
|
|
|
case 0x1008FF19: // XF86XK_Mail:
|
|
|
|
keysym = FL_Mail;
|
|
|
|
break;
|
|
|
|
case 0x1008FF1B: // XF86XK_Search:
|
|
|
|
keysym = FL_Search;
|
|
|
|
break;
|
|
|
|
case 0x1008FF26: // XF86XK_Back:
|
|
|
|
keysym = FL_Back;
|
|
|
|
break;
|
|
|
|
case 0x1008FF27: // XF86XK_Forward:
|
|
|
|
keysym = FL_Forward;
|
|
|
|
break;
|
|
|
|
case 0x1008FF28: // XF86XK_Stop:
|
|
|
|
keysym = FL_Stop;
|
|
|
|
break;
|
|
|
|
case 0x1008FF29: // XF86XK_Refresh:
|
|
|
|
keysym = FL_Refresh;
|
|
|
|
break;
|
|
|
|
case 0x1008FF2F: // XF86XK_Sleep:
|
|
|
|
keysym = FL_Sleep;
|
|
|
|
break;
|
|
|
|
case 0x1008FF30: // XF86XK_Favorites:
|
|
|
|
keysym = FL_Favorites;
|
|
|
|
break;
|
|
|
|
}
|
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...
|
1999-04-23 10:55:53 +04:00
|
|
|
unsigned long keysym1 = XKeycodeToKeysym(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;
|
|
|
|
buffer[0] = char(keysym1) & 0x7F;
|
|
|
|
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
|
|
|
|
2000-11-20 05:49:40 +03:00
|
|
|
set_event_xy();
|
|
|
|
Fl::e_is_click = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
break;}
|
|
|
|
|
2002-04-10 05:32:03 +04:00
|
|
|
case ButtonPress:
|
|
|
|
Fl::e_keysym = FL_Button + xevent.xbutton.button;
|
|
|
|
set_event_xy();
|
|
|
|
if (xevent.xbutton.button == Button4) {
|
|
|
|
Fl::e_dy = -1; // Up
|
|
|
|
event = FL_MOUSEWHEEL;
|
|
|
|
} else if (xevent.xbutton.button == Button5) {
|
|
|
|
Fl::e_dy = +1; // Down
|
|
|
|
event = FL_MOUSEWHEEL;
|
|
|
|
} else {
|
|
|
|
Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
|
|
|
|
event = FL_PUSH;
|
|
|
|
checkdouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
fl_xmousewin = window;
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-04-10 05:32:03 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MotionNotify:
|
|
|
|
set_event_xy();
|
|
|
|
# if CONSOLIDATE_MOTION
|
|
|
|
send_motion = fl_xmousewin = window;
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-04-10 05:32:03 +04:00
|
|
|
return 0;
|
|
|
|
# else
|
|
|
|
event = FL_MOVE;
|
|
|
|
fl_xmousewin = window;
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-04-10 05:32:03 +04:00
|
|
|
break;
|
|
|
|
# endif
|
|
|
|
|
|
|
|
case ButtonRelease:
|
|
|
|
Fl::e_keysym = FL_Button + xevent.xbutton.button;
|
|
|
|
set_event_xy();
|
|
|
|
Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
|
|
|
|
if (xevent.xbutton.button == Button4 ||
|
|
|
|
xevent.xbutton.button == Button5) return 0;
|
|
|
|
event = FL_RELEASE;
|
|
|
|
|
|
|
|
fl_xmousewin = window;
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2002-04-10 05:32:03 +04:00
|
|
|
break;
|
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
case EnterNotify:
|
1998-12-29 17:08:07 +03:00
|
|
|
if (xevent.xcrossing.detail == NotifyInferior) break;
|
1998-10-06 22:21:25 +04:00
|
|
|
// XInstallColormap(fl_display, Fl_X::i(window)->colormap);
|
1999-01-05 20:53:00 +03:00
|
|
|
set_event_xy();
|
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
|
|
|
|
|
|
|
fl_xmousewin = window;
|
2003-03-09 05:00:06 +03:00
|
|
|
in_a_window = true;
|
2010-12-22 10:09:25 +03:00
|
|
|
{ XIMStyles *xim_styles = NULL;
|
|
|
|
if(!fl_xim_im || XGetIMValues(fl_xim_im, XNQueryInputStyle, &xim_styles, NULL, NULL)) {
|
|
|
|
fl_init_xim();
|
|
|
|
}
|
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;
|
1999-01-05 20:53:00 +03:00
|
|
|
set_event_xy();
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl::e_state = xevent.xcrossing.state << 16;
|
2002-04-10 05:32:03 +04:00
|
|
|
fl_xmousewin = 0;
|
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);
|
|
|
|
|
|
|
|
// tell Fl_Window about it and set flag to prevent echoing:
|
|
|
|
resize_bug_fix = window;
|
|
|
|
window->resize(X, Y, W, H);
|
|
|
|
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;
|
|
|
|
window->position(xpos, ypos);
|
|
|
|
}
|
2004-09-11 23:32:43 +04:00
|
|
|
break;
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return Fl::handle(event, window);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void Fl_Window::resize(int X,int Y,int W,int H) {
|
2004-05-16 02:58:19 +04:00
|
|
|
int is_a_move = (X != x() || Y != y());
|
1998-10-20 01:00:26 +04:00
|
|
|
int is_a_resize = (W != w() || H != h());
|
2010-11-21 18:01:58 +03:00
|
|
|
int is_a_enlarge = (W > w() || H > h());
|
1998-10-20 01:00:26 +04:00
|
|
|
int resize_from_program = (this != resize_bug_fix);
|
|
|
|
if (!resize_from_program) resize_bug_fix = 0;
|
2009-09-27 16:06:35 +04:00
|
|
|
if (is_a_move && resize_from_program) set_flag(FORCE_POSITION);
|
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) {
|
|
|
|
Fl_Group::resize(X,Y,W,H);
|
2010-11-21 18:01:58 +03:00
|
|
|
if (shown()) {redraw(); if(is_a_enlarge) i->wait_for_expose = 1;}
|
1998-10-20 01:00:26 +04:00
|
|
|
} else {
|
|
|
|
x(X); y(Y);
|
|
|
|
}
|
2004-05-14 01:02:41 +04:00
|
|
|
|
|
|
|
if (resize_from_program && is_a_resize && !resizable()) {
|
|
|
|
size_range(w(), h(), w(), h());
|
|
|
|
}
|
|
|
|
|
2005-12-14 04:03:13 +03:00
|
|
|
if (resize_from_program && shown()) {
|
2000-01-11 11:20:02 +03:00
|
|
|
if (is_a_resize) {
|
|
|
|
if (!resizable()) size_range(w(),h(),w(),h());
|
2004-05-16 02:58:19 +04:00
|
|
|
if (is_a_move) {
|
2009-03-07 18:15:29 +03:00
|
|
|
XMoveResizeWindow(fl_display, i->xid, X, Y, W>0 ? W : 1, H>0 ? H : 1);
|
2004-05-16 02:58:19 +04:00
|
|
|
} else {
|
2009-03-07 18:15:29 +03:00
|
|
|
XResizeWindow(fl_display, i->xid, W>0 ? W : 1, H>0 ? H : 1);
|
2004-05-16 02:58:19 +04:00
|
|
|
}
|
2005-12-14 04:03:13 +03:00
|
|
|
} else
|
1998-10-06 22:21:25 +04:00
|
|
|
XMoveWindow(fl_display, i->xid, X, Y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
Fl_X* xp = new Fl_X;
|
|
|
|
xp->xid = winxid;
|
|
|
|
xp->other_xid = 0;
|
|
|
|
xp->setwindow(win);
|
|
|
|
xp->next = Fl_X::first;
|
|
|
|
xp->region = 0;
|
|
|
|
xp->wait_for_expose = 1;
|
2002-10-20 10:06:31 +04:00
|
|
|
xp->backbuffer_bad = 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...
|
|
|
|
|
2009-03-07 18:15:29 +03:00
|
|
|
char fl_show_iconic; // hack for iconize()
|
1998-10-06 22:21:25 +04:00
|
|
|
int fl_background_pixel = -1; // hack to speed up bg box drawing
|
|
|
|
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
|
|
|
|
|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
|
2009-09-27 16:06:35 +04:00
|
|
|
if (!(win->flags() & Fl_Widget::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;
|
2005-09-09 13:52:16 +04:00
|
|
|
Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y);
|
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.
|
2007-06-18 12:43:37 +04:00
|
|
|
if (win->parent() && !Fl_X::i(win->window())) {
|
|
|
|
win->set_visible();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
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;
|
2002-08-09 07:17:30 +04: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
|
|
|
}
|
|
|
|
if (fl_background_pixel >= 0) {
|
|
|
|
attr.background_pixel = fl_background_pixel;
|
|
|
|
fl_background_pixel = -1;
|
|
|
|
mask |= CWBackPixel;
|
|
|
|
}
|
1998-11-12 17:14:57 +03:00
|
|
|
|
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,
|
|
|
|
X, Y, W, H,
|
|
|
|
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
|
|
|
|
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:
|
2002-08-09 07:17:30 +04:00
|
|
|
xp->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];
|
|
|
|
char *p; const char *q;
|
|
|
|
// truncate on any punctuation, because they break XResource lookup:
|
2002-08-09 07:17:30 +04:00
|
|
|
for (p = buffer, q = win->xclass(); isalnum(*q)||(*q&128);) *p++ = *q++;
|
1998-10-06 22:21:25 +04:00
|
|
|
*p++ = 0;
|
|
|
|
// create the capitalized version:
|
|
|
|
q = buffer;
|
|
|
|
*p = toupper(*q++); if (*p++ == 'X') *p++ = toupper(*q++);
|
|
|
|
while ((*p++ = *q++));
|
2002-08-09 07:17:30 +04:00
|
|
|
XChangeProperty(fl_display, xp->xid, XA_WM_CLASS, XA_STRING, 8, 0,
|
2009-03-07 18:15:29 +03:00
|
|
|
(unsigned char *)buffer, p-buffer-1);
|
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
|
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
|
|
|
|
2002-03-25 05:36:59 +03:00
|
|
|
// Make it receptive to DnD:
|
2004-02-26 06:06:41 +03:00
|
|
|
long version = 4;
|
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;
|
1998-10-06 22:21:25 +04:00
|
|
|
if (fl_show_iconic) {
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->flags |= StateHint;
|
|
|
|
hints->initial_state = IconicState;
|
1998-10-06 22:21:25 +04:00
|
|
|
fl_show_iconic = 0;
|
2000-04-25 11:48:07 +04:00
|
|
|
showit = 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
2002-08-09 07:17:30 +04:00
|
|
|
if (win->icon()) {
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->icon_pixmap = (Pixmap)win->icon();
|
|
|
|
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);
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Send X window stuff that can be changed over time:
|
|
|
|
|
|
|
|
void Fl_X::sendxjunk() {
|
2001-10-18 22:53:20 +04:00
|
|
|
if (w->parent() || w->override()) return; // it's not a window manager window!
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
if (!w->size_range_set) { // default size_range based on resizable():
|
|
|
|
if (w->resizable()) {
|
|
|
|
Fl_Widget *o = w->resizable();
|
|
|
|
int minw = o->w(); if (minw > 100) minw = 100;
|
|
|
|
int minh = o->h(); if (minh > 100) minh = 100;
|
|
|
|
w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
|
|
|
|
} else {
|
|
|
|
w->size_range(w->w(), w->h(), w->w(), w->h());
|
|
|
|
}
|
|
|
|
return; // because this recursively called here
|
|
|
|
}
|
|
|
|
|
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?
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->min_width = w->minw;
|
|
|
|
hints->min_height = w->minh;
|
|
|
|
hints->max_width = w->maxw;
|
|
|
|
hints->max_height = w->maxh;
|
|
|
|
hints->width_inc = w->dw;
|
|
|
|
hints->height_inc = w->dh;
|
|
|
|
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:
|
2003-04-03 08:28:15 +04:00
|
|
|
if (hints->max_width < hints->min_width) hints->max_width = Fl::w();
|
|
|
|
if (hints->max_height < hints->min_height) hints->max_height = Fl::h();
|
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;
|
1998-10-06 22:21:25 +04:00
|
|
|
if (w->aspect) {
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2009-09-27 16:06:35 +04:00
|
|
|
if (w->flags() & Fl_Widget::FORCE_POSITION) {
|
2003-04-03 08:28:15 +04:00
|
|
|
hints->flags |= USPosition;
|
|
|
|
hints->x = w->x();
|
|
|
|
hints->y = w->y();
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!w->border()) {
|
|
|
|
prop[0] |= 2; // MWM_HINTS_DECORATIONS
|
|
|
|
prop[2] = 0; // no decorations
|
|
|
|
}
|
|
|
|
|
2003-04-03 08:28:15 +04:00
|
|
|
XSetWMNormalHints(fl_display, xid, hints);
|
1998-10-06 22:21:25 +04:00
|
|
|
XChangeProperty(fl_display, xid,
|
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
|
|
|
}
|
|
|
|
|
|
|
|
void Fl_Window::size_range_() {
|
|
|
|
size_range_set = 1;
|
|
|
|
if (shown()) i->sendxjunk();
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// returns pointer to the filename, or null if name ends with '/'
|
2002-03-26 00:08:42 +03:00
|
|
|
const char *fl_filename_name(const char *name) {
|
1998-10-06 22:21:25 +04:00
|
|
|
const char *p,*q;
|
2002-04-07 22:31:55 +04:00
|
|
|
if (!name) return (0);
|
1998-10-06 22:21:25 +04:00
|
|
|
for (p=q=name; *p;) if (*p++ == '/') q = p;
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fl_Window::label(const char *name,const char *iname) {
|
|
|
|
Fl_Widget::label(name);
|
|
|
|
iconlabel_ = iname;
|
|
|
|
if (shown() && !parent()) {
|
|
|
|
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);
|
|
|
|
XChangeProperty(fl_display, i->xid, fl_NET_WM_NAME, fl_XaUtf8String, 8, 0, (uchar*)name, namelen); // utf8
|
|
|
|
XChangeProperty(fl_display, i->xid, XA_WM_NAME, XA_STRING, 8, 0, (uchar*)name, namelen); // non-utf8
|
|
|
|
XChangeProperty(fl_display, i->xid, fl_NET_WM_ICON_NAME, fl_XaUtf8String, 8, 0, (uchar*)iname, inamelen); // utf8
|
|
|
|
XChangeProperty(fl_display, i->xid, 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:
|
|
|
|
|
|
|
|
// If the box is a filled rectangle, we can make the redisplay *look*
|
|
|
|
// faster by using X's background pixel erasing. We can make it
|
|
|
|
// actually *be* faster by drawing the frame only, this is done by
|
2001-11-22 18:35:02 +03:00
|
|
|
// setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
|
1998-10-06 22:21:25 +04:00
|
|
|
//
|
|
|
|
// On XFree86 (and prehaps all X's) this has a problem if the window
|
|
|
|
// is resized while a save-behind window is atop it. The previous
|
2009-03-22 22:21:34 +03:00
|
|
|
// contents are restored to the area, but this assumes the area
|
1998-10-06 22:21:25 +04:00
|
|
|
// is cleared to background color. So this is disabled in this version.
|
|
|
|
// Fl_Window *fl_boxcheat;
|
2010-10-28 22:02:20 +04:00
|
|
|
static inline int can_boxcheat(uchar b) {return (b==1 || ((b&2) && b<=15));}
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
void Fl_Window::show() {
|
2002-01-03 21:28:37 +03:00
|
|
|
image(Fl::scheme_bg_);
|
|
|
|
if (Fl::scheme_bg_) {
|
|
|
|
labeltype(FL_NORMAL_LABEL);
|
|
|
|
align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
|
|
|
|
} else {
|
|
|
|
labeltype(FL_NO_LABEL);
|
|
|
|
}
|
2006-06-14 15:01:03 +04:00
|
|
|
Fl_Tooltip::exit(this);
|
1998-10-06 22:21:25 +04:00
|
|
|
if (!shown()) {
|
|
|
|
fl_open_display();
|
2007-02-02 22:37:30 +03:00
|
|
|
// Don't set background pixel for double-buffered windows...
|
|
|
|
if (type() == FL_WINDOW && can_boxcheat(box())) {
|
|
|
|
fl_background_pixel = int(fl_xpixel(color()));
|
|
|
|
}
|
1998-10-06 22:21:25 +04:00
|
|
|
Fl_X::make_xid(this);
|
|
|
|
} else {
|
|
|
|
XMapRaised(fl_display, i->xid);
|
|
|
|
}
|
2010-03-16 21:27:19 +03:00
|
|
|
#ifdef USE_PRINT_BUTTON
|
2010-03-14 21:07:24 +03:00
|
|
|
void preparePrintFront(void);
|
|
|
|
preparePrintFront();
|
2010-03-16 21:27:19 +03:00
|
|
|
#endif
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Window fl_window;
|
|
|
|
Fl_Window *Fl_Window::current_;
|
|
|
|
GC fl_gc;
|
|
|
|
|
|
|
|
// make X drawing go into this window (called by subclass flush() impl.)
|
|
|
|
void Fl_Window::make_current() {
|
2009-03-07 18:15:29 +03:00
|
|
|
static GC gc; // the GC used by all X windows
|
1998-10-06 22:21:25 +04:00
|
|
|
if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0);
|
|
|
|
fl_window = i->xid;
|
|
|
|
fl_gc = gc;
|
|
|
|
current_ = this;
|
|
|
|
fl_clip_region(0);
|
2008-09-25 22:26:33 +04:00
|
|
|
|
2011-01-06 13:24:58 +03:00
|
|
|
#ifdef FLTK_USE_CAIRO
|
2008-09-25 22:26:33 +04:00
|
|
|
// update the cairo_t context
|
|
|
|
if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this);
|
|
|
|
#endif
|
2011-01-12 12:24:03 +03:00
|
|
|
}
|
2008-09-25 22:26:33 +04:00
|
|
|
|
2011-05-21 14:05:19 +04:00
|
|
|
Window fl_xid_(const Fl_Window *w) {
|
|
|
|
Fl_X *temp = Fl_X::i(w);
|
|
|
|
return temp ? temp->xid : 0;
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
2011-05-30 16:33:51 +04:00
|
|
|
static void decorated_win_size(Fl_Window *win, int &w, int &h)
|
2011-04-16 01:38:05 +04:00
|
|
|
{
|
2011-05-30 16:33:51 +04:00
|
|
|
w = win->w();
|
|
|
|
h = win->h();
|
|
|
|
if (!win->shown() || win->parent() || !win->border() || !win->visible()) return;
|
2011-04-16 01:38:05 +04:00
|
|
|
Window root, parent, *children;
|
2011-05-30 16:33:51 +04:00
|
|
|
unsigned n = 0;
|
|
|
|
Status status = XQueryTree(fl_display, Fl_X::i(win)->xid, &root, &parent, &children, &n);
|
|
|
|
if (status != 0 && n) XFree(children);
|
2011-05-29 22:01:17 +04:00
|
|
|
// when compiz is used, root and parent are the same window
|
|
|
|
// and I don't know where to find the window decoration
|
2011-05-30 16:33:51 +04:00
|
|
|
if (status == 0 || root == parent) return;
|
2011-04-16 01:38:05 +04:00
|
|
|
XWindowAttributes attributes;
|
|
|
|
XGetWindowAttributes(fl_display, parent, &attributes);
|
2011-05-30 16:33:51 +04:00
|
|
|
w = attributes.width;
|
|
|
|
h = attributes.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Fl_Window::decorated_h()
|
|
|
|
{
|
|
|
|
int w, h;
|
|
|
|
decorated_win_size(this, w, h);
|
|
|
|
return h;
|
2011-04-16 01:38:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int Fl_Window::decorated_w()
|
|
|
|
{
|
2011-05-30 16:33:51 +04:00
|
|
|
int w, h;
|
|
|
|
decorated_win_size(this, w, h);
|
|
|
|
return w;
|
2011-04-16 01:38:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
|
|
|
|
{
|
2011-05-30 16:33:51 +04:00
|
|
|
if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
|
2011-04-16 01:38:05 +04:00
|
|
|
this->print_widget(win, x_offset, y_offset);
|
|
|
|
return;
|
2011-05-29 20:29:09 +04:00
|
|
|
}
|
2011-04-16 01:38:05 +04:00
|
|
|
Fl_Display_Device::display_device()->set_current();
|
|
|
|
win->show();
|
|
|
|
Fl::check();
|
|
|
|
win->make_current();
|
|
|
|
Window root, parent, *children, child_win, from;
|
2011-05-29 20:29:09 +04:00
|
|
|
unsigned n = 0;
|
|
|
|
int bx, bt, do_it;
|
2011-04-16 01:38:05 +04:00
|
|
|
from = fl_window;
|
2011-05-29 20:29:09 +04:00
|
|
|
do_it = (XQueryTree(fl_display, fl_window, &root, &parent, &children, &n) != 0 &&
|
|
|
|
XTranslateCoordinates(fl_display, fl_window, parent, 0, 0, &bx, &bt, &child_win) == True);
|
|
|
|
if (n) XFree(children);
|
2011-05-29 22:01:17 +04:00
|
|
|
// hack to bypass STR #2648: when compiz is used, root and parent are the same window
|
|
|
|
// and I don't know where to find the window decoration
|
|
|
|
if (do_it && root == parent) do_it = 0;
|
2011-05-29 20:29:09 +04:00
|
|
|
if (!do_it) {
|
|
|
|
this->set_current();
|
|
|
|
this->print_widget(win, x_offset, y_offset);
|
|
|
|
return;
|
|
|
|
}
|
2011-04-16 01:38:05 +04:00
|
|
|
fl_window = parent;
|
2011-04-23 16:27:58 +04:00
|
|
|
uchar *top_image = 0, *left_image = 0, *right_image = 0, *bottom_image = 0;
|
2011-04-16 01:38:05 +04:00
|
|
|
top_image = fl_read_image(NULL, 0, 0, - (win->w() + 2 * bx), bt);
|
|
|
|
if (bx) {
|
|
|
|
left_image = fl_read_image(NULL, 0, bt, -bx, win->h() + bx);
|
|
|
|
right_image = fl_read_image(NULL, win->w() + bx, bt, -bx, win->h() + bx);
|
|
|
|
bottom_image = fl_read_image(NULL, 0, bt + win->h(), -(win->w() + 2*bx), bx);
|
|
|
|
}
|
|
|
|
fl_window = from;
|
|
|
|
this->set_current();
|
2011-05-30 16:33:51 +04:00
|
|
|
if (top_image) {
|
|
|
|
fl_draw_image(top_image, x_offset, y_offset, win->w() + 2 * bx, bt, 3);
|
|
|
|
delete[] top_image;
|
|
|
|
}
|
2011-04-16 01:38:05 +04:00
|
|
|
if (bx) {
|
|
|
|
if (left_image) fl_draw_image(left_image, x_offset, y_offset + bt, bx, win->h() + bx, 3);
|
|
|
|
if (right_image) fl_draw_image(right_image, x_offset + win->w() + bx, y_offset + bt, bx, win->h() + bx, 3);
|
|
|
|
if (bottom_image) fl_draw_image(bottom_image, x_offset, y_offset + bt + win->h(), win->w() + 2*bx, bx, 3);
|
|
|
|
if (left_image) delete[] left_image;
|
|
|
|
if (right_image) delete[] right_image;
|
|
|
|
if (bottom_image) delete[] bottom_image;
|
|
|
|
}
|
|
|
|
this->print_widget( win, x_offset + bx, y_offset + bt );
|
|
|
|
}
|
|
|
|
|
2010-03-16 21:27:19 +03:00
|
|
|
#ifdef USE_PRINT_BUTTON
|
|
|
|
// to test the Fl_Printer class creating a "Print front window" button in a separate window
|
2010-03-14 21:07:24 +03:00
|
|
|
// contains also preparePrintFront call above
|
|
|
|
#include <FL/Fl_Printer.H>
|
|
|
|
#include <FL/Fl_Button.H>
|
|
|
|
void printFront(Fl_Widget *o, void *data)
|
|
|
|
{
|
|
|
|
Fl_Printer printer;
|
|
|
|
o->window()->hide();
|
|
|
|
Fl_Window *win = Fl::first_window();
|
|
|
|
if(!win) return;
|
|
|
|
int w, h;
|
|
|
|
if( printer.start_job(1) ) { o->window()->show(); return; }
|
|
|
|
if( printer.start_page() ) { o->window()->show(); return; }
|
|
|
|
printer.printable_rect(&w,&h);
|
|
|
|
// scale the printer device so that the window fits on the page
|
|
|
|
float scale = 1;
|
2011-04-16 01:38:05 +04:00
|
|
|
int ww = win->decorated_w();
|
|
|
|
int wh = win->decorated_h();
|
|
|
|
if (ww > w || wh > h) {
|
|
|
|
scale = (float)w/ww;
|
|
|
|
if ((float)h/wh < scale) scale = (float)h/wh;
|
2010-03-14 21:07:24 +03:00
|
|
|
printer.scale(scale, scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
// #define ROTATE 20.0
|
|
|
|
#ifdef ROTATE
|
|
|
|
printer.scale(scale * 0.8, scale * 0.8);
|
|
|
|
printer.printable_rect(&w, &h);
|
|
|
|
printer.origin(w/2, h/2 );
|
|
|
|
printer.rotate(ROTATE);
|
|
|
|
printer.print_widget( win, - win->w()/2, - win->h()/2 );
|
2010-03-25 17:04:43 +03:00
|
|
|
//printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 );
|
2011-04-16 01:38:05 +04:00
|
|
|
#else
|
|
|
|
printer.print_window(win);
|
2010-03-14 21:07:24 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
printer.end_page();
|
|
|
|
printer.end_job();
|
|
|
|
o->window()->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void preparePrintFront(void)
|
|
|
|
{
|
|
|
|
static int first=1;
|
|
|
|
if(!first) return;
|
|
|
|
first=0;
|
|
|
|
static Fl_Window w(0,0,150,30);
|
|
|
|
static Fl_Button b(0,0,w.w(),w.h(), "Print front window");
|
|
|
|
b.callback(printFront);
|
|
|
|
w.end();
|
|
|
|
w.show();
|
|
|
|
}
|
2010-03-16 21:27:19 +03:00
|
|
|
#endif // USE_PRINT_BUTTON
|
2010-03-14 21:07:24 +03:00
|
|
|
|
1998-10-06 22:21:25 +04:00
|
|
|
#endif
|
1998-10-20 00:46:58 +04:00
|
|
|
|
|
|
|
//
|
2005-02-01 06:13:01 +03:00
|
|
|
// End of "$Id$".
|
1998-10-20 00:46:58 +04:00
|
|
|
//
|