summaryrefslogtreecommitdiff
path: root/src/Fl_x.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/Fl_x.cxx')
-rw-r--r--src/Fl_x.cxx807
1 files changed, 807 insertions, 0 deletions
diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx
new file mode 100644
index 000000000..2d5a5ab46
--- /dev/null
+++ b/src/Fl_x.cxx
@@ -0,0 +1,807 @@
+// Fl_x.C
+
+// fltk (Fast Light Tool Kit) version 0.99
+// Copyright (C) 1998 Bill Spitzak
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+// USA.
+
+// Written by Bill Spitzak spitzak@d2.com
+
+#ifdef WIN32
+#include "Fl_win32.C"
+#else
+
+#include <config.h>
+#include <FL/Fl.H>
+#include <FL/x.H>
+#include <FL/Fl_Window.H>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+////////////////////////////////////////////////////////////////
+// interface to poll/select call:
+
+#if HAVE_POLL
+#include <poll.h>
+#else
+struct pollfd {int fd; short events; short revents;};
+#define POLLIN 1
+#define POLLOUT 4
+#define POLLERR 8
+#ifdef __sgi // fix bugs in Irix's select header:
+//inline static void bzero(void *b, int l) {memset(b,0,l);}
+extern "C" int select( int, fd_set *, fd_set *, fd_set *, struct timeval * );
+#endif
+#ifdef hpux // fix from wm2:
+#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
+#endif
+#ifdef __EMX__
+#include <sys/select.h>
+#endif
+#endif
+
+#define MAXFD 8
+#if !HAVE_POLL
+static fd_set fdsets[3];
+static int maxfd;
+#endif
+static int nfds;
+static struct pollfd fds[MAXFD];
+static struct {
+ void (*cb)(int, void*);
+ void* arg;
+} fd[MAXFD];
+
+void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
+ int i;
+ if (nfds < MAXFD) {i = nfds; nfds++;} else {i = MAXFD-1;}
+ fds[i].fd = n;
+ fds[i].events = events;
+#if !HAVE_POLL
+ if (events & POLLIN) FD_SET(n, &fdsets[0]);
+ if (events & POLLOUT) FD_SET(n, &fdsets[1]);
+ if (events & POLLERR) FD_SET(n, &fdsets[2]);
+ if (n > maxfd) maxfd = n;
+#endif
+ fd[i].cb = cb;
+ fd[i].arg = v;
+}
+
+void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
+ Fl::add_fd(fd,POLLIN,cb,v);
+}
+
+void Fl::remove_fd(int n) {
+ int i,j;
+ for (i=j=0; i<nfds; i++) {
+ if (fds[i].fd == n);
+ else {if (j<i) {fd[j]=fd[i]; fds[j]=fds[i];} j++;}
+ }
+ nfds = j;
+#if !HAVE_POLL
+ FD_CLR(n, &fdsets[0]);
+ FD_CLR(n, &fdsets[1]);
+ FD_CLR(n, &fdsets[2]);
+ if (n == maxfd) maxfd--;
+#endif
+}
+
+int fl_ready() {
+ if (XQLength(fl_display)) return 1;
+#if HAVE_POLL
+ return ::poll(fds, nfds, 0);
+#else
+ timeval t;
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ fd_set fdt[3];
+ fdt[0] = fdsets[0];
+ fdt[1] = fdsets[1];
+ fdt[2] = fdsets[2];
+ return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
+#endif
+}
+
+static void do_queued_events() {
+ while (XEventsQueued(fl_display,QueuedAfterReading)) {
+ XEvent xevent;
+ XNextEvent(fl_display, &xevent);
+ fl_handle(xevent);
+ }
+}
+
+double fl_wait(int timeout_flag, double time) {
+
+ // 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:
+ if (XQLength(fl_display)) {do_queued_events(); return time;}
+
+#if !HAVE_POLL
+ fd_set fdt[3];
+ fdt[0] = fdsets[0];
+ fdt[1] = fdsets[1];
+ fdt[2] = fdsets[2];
+#endif
+ int n;
+
+ if (!timeout_flag) {
+#if HAVE_POLL
+ n = ::poll(fds, nfds, -1);
+#else
+ n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
+#endif
+ } else {
+#if HAVE_POLL
+ int n = ::poll(fds, nfds, time > 0.0 ? int(time*1000) : 0);
+#else
+ timeval t;
+ if (time <= 0.0) {
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ } else {
+ t.tv_sec = int(time);
+ t.tv_usec = int(1000000 * (time-t.tv_sec));
+ }
+ n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
+#endif
+ }
+ if (n > 0) {
+ for (int i=0; i<nfds; i++) {
+#if HAVE_POLL
+ if (fds[i].revents) fd[i].cb(fds[i].fd, fd[i].arg);
+#else
+ int f = fds[i].fd;
+ short revents = 0;
+ if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
+ if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
+ if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
+ if (fds[i].events & revents) fd[i].cb(f, fd[i].arg);
+#endif
+ }
+ }
+ return time;
+}
+
+////////////////////////////////////////////////////////////////
+
+Display *fl_display;
+int fl_screen;
+XVisualInfo *fl_visual;
+Colormap fl_colormap;
+
+static Atom wm_delete_window;
+static Atom wm_protocols;
+static Atom _motif_wm_hints;
+
+static void fd_callback(int,void *) {do_queued_events();}
+
+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;
+}
+
+void fl_open_display() {
+ if (fl_display) return;
+
+ XSetIOErrorHandler(io_error_handler);
+ XSetErrorHandler(xerror_handler);
+
+ Display *d = XOpenDisplay(0);
+ if (!d) Fl::fatal("Can't open display: %s",XDisplayName(0));
+
+ fl_display = d;
+
+ wm_delete_window = XInternAtom(d,"WM_DELETE_WINDOW",0);
+ wm_protocols = XInternAtom(d,"WM_PROTOCOLS",0);
+ _motif_wm_hints = XInternAtom(d,"_MOTIF_WM_HINTS",0);
+ Fl::add_fd(ConnectionNumber(d), POLLIN, fd_callback);
+
+ fl_screen = DefaultScreen(fl_display);
+// construct an XVisualInfo that matches the default Visual:
+ XVisualInfo templt; int num;
+ templt.visualid = XVisualIDFromVisual(DefaultVisual(fl_display,fl_screen));
+ fl_visual = XGetVisualInfo(fl_display, VisualIDMask, &templt, &num);
+ fl_colormap = DefaultColormap(fl_display,fl_screen);
+}
+
+void fl_close_display() {
+ Fl::remove_fd(ConnectionNumber(fl_display));
+ XCloseDisplay(fl_display);
+}
+
+int Fl::h() {
+ fl_open_display();
+ return DisplayHeight(fl_display,fl_screen);
+}
+
+int Fl::w() {
+ fl_open_display();
+ return DisplayWidth(fl_display,fl_screen);
+}
+
+void Fl::get_mouse(int &x, int &y) {
+ 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);
+ x = mx;
+ y = my;
+}
+
+////////////////////////////////////////////////////////////////
+
+extern Fl_Window *fl_xfocus; // in Fl.C
+extern Fl_Window *fl_xmousewin; // in Fl.C
+void fl_fix_focus(); // in Fl.C
+
+////////////////////////////////////////////////////////////////
+
+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;
+
+static void set_event_xy() {
+ 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;
+ fl_event_time = fl_xevent->xbutton.time;
+#ifdef __sgi
+ // get the meta key off PC keyboards:
+ if (fl_key_vector[18]&0x18) Fl::e_state |= FL_META;
+#endif
+ // turn off is_click if enough time or mouse movement has passed:
+ if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3
+ || fl_event_time >= ptime+1000)
+ Fl::e_is_click = 0;
+}
+
+// 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;
+
+////////////////////////////////////////////////////////////////
+
+int fl_handle(const XEvent& xevent)
+{
+ fl_xevent = &xevent;
+
+ switch (xevent.type) { // events where we don't care about window
+
+ case KeymapNotify:
+ memcpy(fl_key_vector, xevent.xkeymap.key_vector, 32);
+ return 0;
+
+ case MappingNotify:
+ XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
+ return 0;
+ }
+
+ int event = 0;
+ Fl_Window* window = fl_find(xevent.xany.window);
+
+ if (window) switch (xevent.type) {
+
+ case ClientMessage:
+ if ((Atom)(xevent.xclient.data.l[0]) == wm_delete_window) event = FL_CLOSE;
+ break;
+
+ case MapNotify:
+ event = FL_SHOW;
+ break;
+
+ case UnmapNotify:
+ event = FL_HIDE;
+ break;
+
+ case Expose:
+ case GraphicsExpose:
+#if 1 // try to keep windows on top even if WM_TRANSIENT_FOR does not work:
+ if (Fl::first_window()->non_modal() && window != Fl::first_window())
+ Fl::first_window()->show();
+#endif
+ window->damage(2, xevent.xexpose.x, xevent.xexpose.y,
+ xevent.xexpose.width, xevent.xexpose.height);
+ return 1;
+
+ case ButtonPress:
+ Fl::e_keysym = FL_Button + xevent.xbutton.button;
+ set_event_xy(); checkdouble();
+ // fix buggy window managers that position window wrong:
+ Fl_X::x(window,Fl::e_x_root-Fl::e_x);
+ Fl_X::y(window,Fl::e_y_root-Fl::e_y);
+ Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
+ event = FL_PUSH;
+ break;
+
+ case MotionNotify:
+ set_event_xy();
+ event = FL_MOVE;
+ break;
+
+ case ButtonRelease:
+ Fl::e_keysym = FL_Button + xevent.xbutton.button;
+ set_event_xy();
+ Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
+ event = FL_RELEASE;
+ break;
+
+ case FocusIn:
+ event = FL_FOCUS;
+ break;
+
+ case FocusOut:
+ event = FL_UNFOCUS;
+ break;
+
+ case KeyPress: {
+ static int got_backspace;
+ static char buffer[21];
+ KeySym keysym;
+ int i = xevent.xkey.keycode; fl_key_vector[i/8] |= (1 << (i%8));
+ int len = XLookupString((XKeyEvent*)&(xevent.xkey),buffer,20,&keysym,0);
+ if (!len && keysym < 0x400) {
+ // turn all latin-2,3,4 characters into 8-bit codes:
+ buffer[0] = char(keysym);
+ len = 1;
+ }
+ // ignore all effects of shift on the keysyms (makes it a lot
+ // easier to program shortcuts!)
+ keysym = XKeycodeToKeysym(fl_display, i, 0);
+#ifdef __sgi
+ // get some missing PC keyboard keys:
+ if (!keysym) switch(i) {
+ case 147: keysym = FL_Meta_L; break;
+ case 148: keysym = FL_Meta_R; break;
+ case 149: keysym = FL_Menu; break;
+ }
+#endif
+ if (!got_backspace) {
+ // Backspace kludge: until user hits the backspace key, assumme
+ // it is missing and use the Delete key for that purpose:
+ if (keysym == FL_Delete) keysym = FL_BackSpace;
+ else if (keysym == FL_BackSpace) got_backspace = 1;
+ }
+ if (keysym >= 0xff95 && keysym < 0xffa0) {
+ // Make NumLock irrelevant (always on):
+ // This lookup table turns the XK_KP_* functions back into the
+ // ascii characters. This won't work on non-PC layout keyboards,
+ // but are there any of those left??
+ buffer[0] = "7486293150."[keysym-0xff95];
+ len = 1;
+ keysym = FL_KP+buffer[0];
+ }
+ buffer[len] = 0;
+ Fl::e_keysym = int(keysym);
+ Fl::e_text = buffer;
+ Fl::e_length = len;
+ set_event_xy(); Fl::e_is_click = 0;
+ if (Fl::event_state(FL_CTRL) && keysym == '-') buffer[0] = 0x1f; // ^_
+ event = FL_KEYBOARD;
+ break;}
+
+ case KeyRelease: {
+ int i = xevent.xkey.keycode; fl_key_vector[i/8] &= ~(1 << (i%8));
+ set_event_xy();}
+ break;
+
+ case EnterNotify:
+ if (xevent.xcrossing.detail == NotifyInferior) break;
+ // XInstallColormap(fl_display, Fl_X::i(window)->colormap);
+ set_event_xy();
+ Fl::e_state = xevent.xcrossing.state << 16;
+ event = FL_ENTER;
+ break;
+
+ case LeaveNotify:
+ if (xevent.xcrossing.detail == NotifyInferior) break;
+ set_event_xy();
+ Fl::e_state = xevent.xcrossing.state << 16;
+ event = FL_LEAVE;
+ break;
+
+ case ConfigureNotify: {
+ int x = xevent.xconfigure.x;
+ int y = xevent.xconfigure.y;
+ // avoid bug (?) in 4DWM, it reports position of 0,0 on resize:
+ if (!x && !y) {
+ Window r, c; int X, Y; unsigned int m;
+ XQueryPointer(fl_display, fl_xid(window), &r, &c, &x, &y, &X, &Y, &m);
+ x = x-X; y = y-Y;
+ }
+ resize_bug_fix = window;
+ window->resize(x, y,
+ xevent.xconfigure.width, xevent.xconfigure.height);
+ return 1;}
+ }
+
+ return Fl::handle(event, window);
+}
+
+////////////////////////////////////////////////////////////////
+
+void Fl_Window::resize(int X,int Y,int W,int H) {
+ if (resize_bug_fix == this)
+ resize_bug_fix = 0;
+ else if (shown()) {
+ // tell X window manager to change window size:
+ if (!(flags()&FL_FORCE_POSITION) && X == x() && Y == y())
+ XResizeWindow(fl_display, i->xid, W>0 ? W : 1, H>0 ? H : 1);
+ else if (W != w() || H != h())
+ XMoveResizeWindow(fl_display, i->xid, X, Y, W>0 ? W : 1, H>0 ? H : 1);
+ else
+ XMoveWindow(fl_display, i->xid, X, Y);
+ }
+ if (X != x() || Y != y()) set_flag(FL_FORCE_POSITION);
+ if (W != w() || H != h()) Fl_Group::resize(X,Y,W,H); else {x(X); y(Y);}
+ // Notice that this does *not* set any redraw bits. I assumme
+ // I will receive damage for the whole window from X. I think
+ // that "ForgetGravity" forces the expose event for the entire
+ // window, but this may not be true on some implementations.
+}
+
+////////////////////////////////////////////////////////////////
+
+// A subclass of Fl_Window may call this to associate an X window it
+// creates with the Fl_Window:
+
+Fl_X* Fl_X::set_xid(Fl_Window* w, Window xid) {
+ Fl_X* x = new Fl_X;
+ x->xid = xid;
+ x->other_xid = 0;
+ x->setwindow(w);
+ x->next = Fl_X::first;
+ x->region = 0;
+ Fl_X::first = x;
+ w->set_visible();
+ w->handle(FL_SHOW); // get child windows to appear
+ fl_fix_focus(); // if this is modal we must fix focus now
+ return x;
+}
+
+// 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...
+
+char fl_show_iconic; // hack for iconize()
+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;
+
+void Fl_X::make_xid(Fl_Window* w, XVisualInfo *visual, Colormap colormap)
+{
+ Fl_Group::current(0); // get rid of very common user bug: forgot end()
+
+ ulong root = w->parent() ?
+ fl_xid(w->window()) : RootWindow(fl_display, fl_screen);
+
+ XSetWindowAttributes attr;
+ int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
+ attr.event_mask = w->parent() ? childEventMask : XEventMask;
+ attr.colormap = colormap;
+ attr.border_pixel = 0;
+ attr.bit_gravity = 0; // StaticGravity;
+ attr.override_redirect = 0;
+ if (Fl::grab()) {
+ attr.save_under = 1; mask |= CWSaveUnder;
+ if (!w->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;}
+ }
+ if (fl_background_pixel >= 0) {
+ attr.background_pixel = fl_background_pixel;
+ fl_background_pixel = -1;
+ mask |= CWBackPixel;
+ }
+ Fl_X* x =
+ set_xid(w, XCreateWindow(fl_display,
+ root,
+ w->x(), w->y(),
+ w->w()>0 ? w->w() : 1,
+ w->h()>0 ? w->h() : 1,
+ 0, // borderwidth
+ visual->depth,
+ InputOutput,
+ visual->visual,
+ mask, &attr));
+ //XInstallColormap(fl_display, colormap);
+
+ if (!w->parent() && !attr.override_redirect) {
+ // Communicate all kinds 'o junk to the X Window Manager:
+
+ w->label(w->label(), w->iconlabel());
+
+ XChangeProperty(fl_display, x->xid, wm_protocols,
+ XA_ATOM, 32, 0, (uchar*)&wm_delete_window, 1);
+
+ // send size limits and border:
+ x->sendxjunk();
+
+ // set the class property, which controls the icon used:
+ if (w->xclass()) {
+ char buffer[1024];
+ char *p; const char *q;
+ // truncate on any punctuation, because they break XResource lookup:
+ for (p = buffer, q = w->xclass(); isalnum(*q)||(*q&128);) *p++ = *q++;
+ *p++ = 0;
+ // create the capitalized version:
+ q = buffer;
+ *p = toupper(*q++); if (*p++ == 'X') *p++ = toupper(*q++);
+ while ((*p++ = *q++));
+ XChangeProperty(fl_display, x->xid, XA_WM_CLASS, XA_STRING, 8, 0,
+ (unsigned char *)buffer, p-buffer-1);
+ }
+
+ if (w->non_modal() && x->next && !fl_disable_transient_for) {
+ // find some other window to be "transient for":
+ Fl_Window* w = x->next->w;
+ while (w->parent()) w = w->window();
+ XSetTransientForHint(fl_display, x->xid, fl_xid(w));
+ }
+
+ if (fl_show_iconic) {
+ XWMHints hints;
+ hints.flags = StateHint;
+ hints.initial_state = 3;
+ XSetWMHints(fl_display, x->xid, &hints);
+ fl_show_iconic = 0;
+ }
+ }
+
+ XMapWindow(fl_display, x->xid);
+}
+
+////////////////////////////////////////////////////////////////
+// Send X window stuff that can be changed over time:
+
+void Fl_X::sendxjunk() {
+ if (w->parent()) return; // it's not a window manager window!
+
+ 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
+ }
+
+ XSizeHints hints;
+ 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;
+
+ // 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};
+
+ if (hints.min_width != hints.max_width ||
+ hints.min_height != hints.max_height) { // resizable
+ hints.flags = PMinSize;
+ if (hints.max_width >= hints.min_width ||
+ hints.max_height >= hints.min_height) {
+ hints.flags = PMinSize|PMaxSize;
+ // 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:
+ if (hints.max_width < hints.min_width) hints.max_width = Fl::w();
+ if (hints.max_height < hints.min_height) hints.max_height = Fl::h();
+ }
+ if (hints.width_inc && hints.height_inc) hints.flags |= PResizeInc;
+ if (w->aspect) {
+ // stupid X! It could insist that the corner go on the
+ // straight line between min and max...
+ 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;
+ }
+ } else { // not resizable:
+ hints.flags = PMinSize|PMaxSize;
+ prop[0] = 1; // MWM_HINTS_FUNCTIONS
+ prop[1] = 1|2|16; // MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE
+ }
+ if (w->non_modal()) {
+ prop[0] = 1; // MWM_HINTS_FUNCTIONS
+ prop[1] |= 8; // turn off MWM_FUNC_MINIMIZE in 4Dwm
+ }
+
+ if (w->flags() & Fl_Window::FL_FORCE_POSITION) {
+ hints.flags |= USPosition;
+ hints.x = w->x();
+ hints.y = w->y();
+ }
+
+ if (!w->border()) {
+ prop[0] |= 2; // MWM_HINTS_DECORATIONS
+ prop[2] = 0; // no decorations
+ }
+
+ XSetWMNormalHints(fl_display, xid, &hints);
+ XChangeProperty(fl_display, xid,
+ _motif_wm_hints, _motif_wm_hints,
+ 32, 0, (unsigned char *)prop, 5);
+}
+
+void Fl_Window::size_range_() {
+ size_range_set = 1;
+ if (shown()) i->sendxjunk();
+}
+
+////////////////////////////////////////////////////////////////
+
+// returns pointer to the filename, or null if name ends with '/'
+const char *filename_name(const char *name) {
+ const char *p,*q;
+ 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 = "";
+ XChangeProperty(fl_display, i->xid, XA_WM_NAME,
+ XA_STRING, 8, 0, (uchar*)name, strlen(name));
+ if (!iname) iname = filename_name(name);
+ XChangeProperty(fl_display, i->xid, XA_WM_ICON_NAME,
+ XA_STRING, 8, 0, (uchar*)iname, strlen(iname));
+ }
+}
+
+////////////////////////////////////////////////////////////////
+// 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
+// setting fl_boxcheat, which is seen by code in fl_drawbox.C:
+//
+// 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
+// contents are restored to the area, but this assummes the area
+// is cleared to background color. So this is disabled in this version.
+// Fl_Window *fl_boxcheat;
+static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);}
+
+void Fl_Window::show() {
+ if (!shown()) {
+ fl_open_display();
+ if (can_boxcheat(box())) fl_background_pixel = int(fl_xpixel(color()));
+ Fl_X::make_xid(this);
+ } else {
+ XMapRaised(fl_display, i->xid);
+ }
+}
+
+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() {
+ static GC gc; // the GC used by all X windows
+ if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0);
+ fl_window = i->xid;
+ fl_gc = gc;
+ current_ = this;
+ fl_clip_region(0);
+}
+
+#include <FL/fl_draw.H>
+
+// Current meaning of damage() bits on a window:
+// 1 = a child needs redrawing
+// 2 = expose events
+// 128 = redraw everything
+
+void Fl_Widget::damage(uchar flags) {
+ if (type() < FL_WINDOW) {
+ damage(flags, x(), y(), w(), h());
+ } else {
+ Fl_X* i = Fl_X::i((Fl_Window*)this);
+ if (i) {
+ if (i->region) {XDestroyRegion(i->region); i->region = 0;}
+ damage_ |= flags;
+ Fl::damage(1);
+ }
+ }
+}
+
+void Fl_Widget::redraw() {damage(~0);}
+
+Region XRectangleRegion(int x, int y, int w, int h); // in fl_rect.C
+
+void Fl_Widget::damage(uchar flags, int X, int Y, int W, int H) {
+ if (type() < FL_WINDOW) {
+ damage_ |= flags;
+ if (parent()) parent()->damage(1,X,Y,W,H);
+ } else {
+ // see if damage covers entire window:
+ if (X<=0 && Y<=0 && W>=w() && H>=h()) {damage(flags); return;}
+ Fl_X* i = Fl_X::i((Fl_Window*)this);
+ if (i) {
+ if (damage()) {
+ // if we already have damage we must merge with existing region:
+ if (i->region) {
+ XRectangle R;
+ R.x = X; R.y = Y; R.width = W; R.height = H;
+ XUnionRectWithRegion(&R, i->region, i->region);
+ }
+ damage_ |= flags;
+ } else {
+ // create a new region:
+ if (i->region) XDestroyRegion(i->region);
+ i->region = XRectangleRegion(X,Y,W,H);
+ damage_ = flags;
+ }
+ Fl::damage(1);
+ }
+ }
+}
+
+void Fl_Window::flush() {
+ make_current();
+//if (damage() == 2 && can_boxcheat(box())) fl_boxcheat = this;
+ fl_clip_region(i->region); i->region = 0;
+ draw();
+}
+
+#endif
+// End of Fl_x.C