summaryrefslogtreecommitdiffstats
path: root/unix/tx
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tx')
-rw-r--r--unix/tx/Makefile.in17
-rw-r--r--unix/tx/TXButton.h124
-rw-r--r--unix/tx/TXCheckbox.h142
-rw-r--r--unix/tx/TXDialog.h101
-rw-r--r--unix/tx/TXEntry.h191
-rw-r--r--unix/tx/TXImage.cxx353
-rw-r--r--unix/tx/TXImage.h97
-rw-r--r--unix/tx/TXLabel.h126
-rw-r--r--unix/tx/TXMenu.cxx186
-rw-r--r--unix/tx/TXMenu.h67
-rw-r--r--unix/tx/TXMsgBox.h112
-rw-r--r--unix/tx/TXScrollbar.cxx119
-rw-r--r--unix/tx/TXScrollbar.h82
-rw-r--r--unix/tx/TXViewport.cxx155
-rw-r--r--unix/tx/TXViewport.h77
-rw-r--r--unix/tx/TXWindow.cxx486
-rw-r--r--unix/tx/TXWindow.h211
17 files changed, 2646 insertions, 0 deletions
diff --git a/unix/tx/Makefile.in b/unix/tx/Makefile.in
new file mode 100644
index 00000000..89c30b17
--- /dev/null
+++ b/unix/tx/Makefile.in
@@ -0,0 +1,17 @@
+
+SRCS = TXWindow.cxx TXScrollbar.cxx TXViewport.cxx TXImage.cxx TXMenu.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+library = libtx.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+ rm -f $(library)
+ $(AR) $(library) $(OBJS)
+ $(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/unix/tx/TXButton.h b/unix/tx/TXButton.h
new file mode 100644
index 00000000..b7472797
--- /dev/null
+++ b/unix/tx/TXButton.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXButton.h
+//
+// A TXButton is a clickable button with some text in it. The button must be
+// big enough to contain the text - if not then it will be resized
+// appropriately.
+//
+
+#ifndef __TXBUTTON_H__
+#define __TXBUTTON_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXButtonCallback's buttonActivate() method is called when a button is
+// activated.
+class TXButton;
+class TXButtonCallback {
+public:
+ virtual void buttonActivate(TXButton* button)=0;
+};
+
+
+class TXButton : public TXWindow, public TXEventHandler {
+public:
+
+ TXButton(Display* dpy_, const char* text_, TXButtonCallback* cb_=0,
+ TXWindow* parent_=0, int w=1, int h=1)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_), down(false),
+ disabled_(false)
+ {
+ setEventHandler(this);
+ setText(text_);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ XSetFont(dpy, gc, defaultFont);
+ addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask);
+ }
+
+ virtual ~TXButton() {
+ XFreeGC(dpy, gc);
+ }
+
+ // setText() changes the text in the button.
+ void setText(const char* text_) {
+ text.buf = rfb::strDup(text_);
+ int textWidth = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+ int textHeight = (defaultFS->ascent + defaultFS->descent);
+ int newWidth = __rfbmax(width(), textWidth + xPad*2 + bevel*2);
+ int newHeight = __rfbmax(height(), textHeight + yPad*2 + bevel*2);
+ if (width() < newWidth || height() < newHeight) {
+ resize(newWidth, newHeight);
+ }
+ }
+
+ // disabled() sets or queries the disabled state of the checkbox. A disabled
+ // checkbox cannot be changed via the user interface.
+ void disabled(bool b) { disabled_ = b; paint(); }
+ bool disabled() { return disabled_; }
+
+private:
+
+ void paint() {
+ int tw = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+ int startx = (width() - tw) / 2;
+ int starty = (height() + defaultFS->ascent - defaultFS->descent) / 2;
+ if (down || disabled_) {
+ drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, darkBg,lightBg);
+ startx++; starty++;
+ } else {
+ drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, lightBg,darkBg);
+ }
+
+ XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+ XDrawString(dpy, win(), gc, startx, starty, text.buf, strlen(text.buf));
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+ case ButtonPress:
+ if (!disabled_) {
+ down = true;
+ paint();
+ }
+ break;
+ case ButtonRelease:
+ if (!down) break;
+ down = false;
+ paint();
+ if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+ ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+ if (cb) cb->buttonActivate(this);
+ }
+ break;
+ }
+ }
+
+ GC gc;
+ rfb::CharArray text;
+ TXButtonCallback* cb;
+ bool down;
+ bool disabled_;
+};
+
+#endif
diff --git a/unix/tx/TXCheckbox.h b/unix/tx/TXCheckbox.h
new file mode 100644
index 00000000..0880b38c
--- /dev/null
+++ b/unix/tx/TXCheckbox.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXCheckbox.h
+//
+// A TXCheckbox has a box which may be "checked" with some text next to it.
+// The checkbox window must be big enough to contain the text - if not then it
+// will be resized appropriately.
+//
+// There are two styles of checkbox: the normal style which uses a tick in a
+// square box, and the radio style which uses a dot inside a circle. The
+// default behaviour when clicking on the checkbox is to toggle it on or off,
+// but this behaviour can be changed by the callback object. In particular to
+// get radiobutton behaviour, the callback must ensure that only one of a set
+// of radiobuttons is selected.
+//
+
+#ifndef __TXCHECKBOX_H__
+#define __TXCHECKBOX_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXCheckboxCallback's checkboxSelect() method is called when the state of a
+// checkbox changes.
+class TXCheckbox;
+class TXCheckboxCallback {
+public:
+ virtual void checkboxSelect(TXCheckbox* checkbox)=0;
+};
+
+
+class TXCheckbox : public TXWindow, public TXEventHandler {
+public:
+ TXCheckbox(Display* dpy_, const char* text_, TXCheckboxCallback* cb_,
+ bool radio_=false, TXWindow* parent_=0, int w=1, int h=1)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_), text(0),
+ boxSize(radio_ ? 12 : 13), boxPad(4),
+ checked_(false), disabled_(false), radio(radio_)
+ {
+ setEventHandler(this);
+ setText(text_);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ XSetFont(dpy, gc, defaultFont);
+ addEventMask(ExposureMask| ButtonPressMask | ButtonReleaseMask);
+ }
+
+ virtual ~TXCheckbox() {
+ XFreeGC(dpy, gc);
+ if (text) free(text);
+ }
+
+ // setText() changes the text in the checkbox.
+ void setText(const char* text_) {
+ if (text) free(text);
+ text = strdup((char*)text_);
+ int textWidth = XTextWidth(defaultFS, text, strlen(text));
+ int textHeight = (defaultFS->ascent + defaultFS->descent);
+ int newWidth = __rfbmax(width(), textWidth + xPad*2 + boxPad*2 + boxSize);
+ int newHeight = __rfbmax(height(), textHeight + yPad*2);
+ if (width() < newWidth || height() < newHeight) {
+ resize(newWidth, newHeight);
+ }
+ }
+
+ // checked() sets or queries the state of the checkbox
+ void checked(bool b) { checked_ = b; paint(); }
+ bool checked() { return checked_; }
+
+ // disabled() sets or queries the disabled state of the checkbox. A disabled
+ // checkbox cannot be changed via the user interface.
+ void disabled(bool b) { disabled_ = b; paint(); }
+ bool disabled() { return disabled_; }
+
+private:
+ void paint() {
+ if (disabled_)
+ drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+ bevel, disabledBg, darkBg, lightBg, radio);
+ else
+ drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+ bevel, enabledBg, darkBg, lightBg, radio);
+ XSetBackground(dpy, gc, disabled_ ? disabledBg : enabledBg);
+ XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+ if (checked_) {
+ Pixmap icon = radio ? dot : tick;
+ int iconSize = radio ? dotSize : tickSize;
+ XCopyPlane(dpy, icon, win(), gc, 0, 0, iconSize, iconSize,
+ xPad + boxPad + (boxSize - iconSize) / 2,
+ (height() - iconSize) / 2, 1);
+ }
+ XDrawString(dpy, win(), gc, xPad + boxSize + boxPad*2,
+ (height() + defaultFS->ascent - defaultFS->descent) / 2,
+ text, strlen(text));
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+ case ButtonPress:
+ break;
+ case ButtonRelease:
+ if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+ ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+ if (!disabled_) {
+ checked_ = !checked_;
+ if (cb) cb->checkboxSelect(this);
+ paint();
+ }
+ }
+ break;
+ }
+ }
+
+ TXCheckboxCallback* cb;
+ GC gc;
+ char* text;
+ int boxSize;
+ int boxPad;
+ bool checked_;
+ bool disabled_;
+ bool radio;
+};
+
+#endif
diff --git a/unix/tx/TXDialog.h b/unix/tx/TXDialog.h
new file mode 100644
index 00000000..c8d601c3
--- /dev/null
+++ b/unix/tx/TXDialog.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXDialog.h
+//
+// A TXDialog is a pop-up dialog window. The dialog can be made visible by
+// calling its show() method. Dialogs can be modal or non-modal. For a modal
+// dialog box, the show() method only returns when the dialog box has been
+// dismissed. For a non-modal dialog box, the show() method returns
+// immediately.
+//
+
+#ifndef __TXDIALOG_H__
+#define __TXDIALOG_H__
+
+#include "TXWindow.h"
+#include <errno.h>
+
+// XXX Lynx/OS 2.3: protos for bzero(), select()
+#ifdef Lynx
+#include <sys/proto.h>
+#endif
+
+class TXDialog : public TXWindow, public TXDeleteWindowCallback {
+public:
+ TXDialog(Display* dpy, int width, int height, const char* name,
+ bool modal_=false)
+ : TXWindow(dpy, width, height), done(false), ok(false), modal(modal_)
+ {
+ toplevel(name, this);
+ resize(width, height);
+ }
+
+ virtual ~TXDialog() {}
+
+ // show() makes the dialog visible. For a modal dialog box, this processes X
+ // events until the done flag has been set, after which it returns the value
+ // of the ok flag. For a non-modal dialog box it always returns true
+ // immediately.
+ bool show() {
+ ok = false;
+ done = false;
+ initDialog();
+ raise();
+ map();
+ if (modal) {
+ while (true) {
+ TXWindow::handleXEvents(dpy);
+ if (done) {
+ return ok;
+ }
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(ConnectionNumber(dpy), &rfds);
+ int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+ if (n < 0) throw rdr::SystemException("select",errno);
+ }
+ }
+ return true;
+ }
+
+ // initDialog() can be overridden in a derived class. Typically it is used
+ // to make sure that checkboxes have the right state, etc.
+ virtual void initDialog() {}
+
+ // resize() is overidden here to re-center the dialog
+ void resize(int w, int h) {
+ TXWindow::resize(w,h);
+ int dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
+ int dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
+ setUSPosition((dpyWidth - width() - 10) / 2, (dpyHeight - height() - 30) / 2);
+ }
+
+protected:
+ virtual void deleteWindow(TXWindow* w) {
+ ok = false;
+ done = true;
+ unmap();
+ }
+
+ bool done;
+ bool ok;
+ bool modal;
+};
+
+#endif
diff --git a/unix/tx/TXEntry.h b/unix/tx/TXEntry.h
new file mode 100644
index 00000000..1785a5f5
--- /dev/null
+++ b/unix/tx/TXEntry.h
@@ -0,0 +1,191 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXEntry.h
+//
+// A TXEntry allows you to enter a single line of text in a window. The entry
+// must be tall enough to contain a line of text - if not then it will be
+// resized appropriately. If the passwd argument to the constructor is true,
+// then the text in the entry will be replaced by asterisks on the screen.
+//
+
+#ifndef __TXENTRY_H__
+#define __TXENTRY_H__
+
+#include "TXWindow.h"
+#include <X11/keysym.h>
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab 0xFE20
+#endif
+#ifndef XK_KP_Delete
+#define XK_KP_Delete 0xFF9F
+#endif
+
+// TXEntryCallback's entryCallback() method is called when one of three special
+// key presses have happened: Enter/Return, forward tab, or backward tab.
+class TXEntry;
+class TXEntryCallback {
+public:
+ enum Detail { ENTER, NEXT_FOCUS, PREV_FOCUS };
+ virtual void entryCallback(TXEntry* entry, Detail detail, Time time)=0;
+};
+
+
+class TXEntry : public TXWindow, public TXEventHandler {
+public:
+
+ TXEntry(Display* dpy_, TXEntryCallback* cb_=0,
+ TXWindow* parent_=0, bool passwd_=false, int w=1, int h=1)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_),
+ passwd(passwd_), disabled_(false), gotFocus(false)
+ {
+ setEventHandler(this);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ addEventMask(ExposureMask | KeyPressMask | FocusChangeMask
+ | ButtonPressMask);
+ text[0] = 0;
+ int textHeight = (defaultFS->ascent + defaultFS->descent);
+ int newHeight = __rfbmax(height(), textHeight + yPad*2 + bevel*2);
+ if (height() < newHeight) {
+ resize(width(), newHeight);
+ }
+ }
+
+ virtual ~TXEntry() {
+ XFreeGC(dpy, gc);
+ // overwrite memory used to store password - not critical, but can avoid
+ // accidental exposure of a password in uninitialised memory.
+ if (passwd)
+ memset(text, 0, maxLen);
+ }
+
+ // getText() gets the text in the entry.
+ const char* getText() { return text; }
+
+ // setText() sets the text in the entry.
+ void setText(const char* text_) {
+ strncpy(text, text_, maxLen-1);
+ text[maxLen-1] = 0;
+ paint();
+ }
+
+ // disabled() sets or queries the disabled state of the entry. A disabled
+ // entry cannot have text entered into it.
+ void disabled(bool b) { disabled_ = b; paint(); }
+ bool disabled() { return disabled_; }
+
+private:
+ void paint() {
+ if (disabled_)
+ drawBevel(gc, 0, 0, width(), height(), bevel, disabledBg,darkBg,lightBg);
+ else
+ drawBevel(gc, 0, 0, width(), height(), bevel, enabledBg, darkBg,lightBg);
+ char* str = text;
+ char stars[maxLen];
+ if (passwd) {
+ int i;
+ for (i = 0; i < (int)strlen(text); i++) stars[i] = '*';
+ stars[i] = 0;
+ str = stars;
+ }
+ int tw = XTextWidth(defaultFS, str, strlen(str));
+ int startx = bevel + xPad;
+ if (startx + tw > width() - 2*bevel) {
+ startx = width() - 2*bevel - tw;
+ }
+ XDrawString(dpy, win(), defaultGC, startx,
+ (height() + defaultFS->ascent - defaultFS->descent) / 2,
+ str, strlen(str));
+ if (!disabled_ && gotFocus)
+ XDrawLine(dpy, win(), defaultGC, startx+tw,
+ (height() - defaultFS->ascent - defaultFS->descent) / 2,
+ startx+tw,
+ (height() + defaultFS->ascent + defaultFS->descent) / 2);
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+
+ case FocusIn:
+ gotFocus = true;
+ paint();
+ break;
+
+ case FocusOut:
+ gotFocus = false;
+ paint();
+ break;
+
+ case ButtonPress:
+ if (!disabled_)
+ XSetInputFocus(dpy, win(), RevertToParent, ev->xbutton.time);
+ break;
+
+ case KeyPress:
+ {
+ if (disabled_ || !gotFocus) break;
+ KeySym keysym;
+ XComposeStatus compose;
+ char buf[10];
+ int count = XLookupString(&ev->xkey, buf, 10, &keysym, &compose);
+ if (count >= 1 && buf[0] >= ' ' && buf[0] <= '~') {
+ if (strlen(text) + count >= maxLen) {
+ XBell(dpy, 0);
+ } else {
+ strncat(text, buf, count);
+ paint();
+ }
+ } else if (keysym == XK_BackSpace || keysym == XK_Delete ||
+ keysym == XK_KP_Delete) {
+ if (strlen(text) > 0) {
+ text[strlen(text)-1] = 0;
+ paint();
+ }
+ } else if (keysym == XK_Return || keysym == XK_KP_Enter ||
+ keysym == XK_Linefeed) {
+ if (cb) cb->entryCallback(this, TXEntryCallback::ENTER,
+ ev->xkey.time);
+ } else if ((keysym == XK_Tab || keysym == XK_KP_Tab)
+ && !(ev->xkey.state & ShiftMask))
+ {
+ if (cb) cb->entryCallback(this, TXEntryCallback::NEXT_FOCUS,
+ ev->xkey.time);
+ } else if (((keysym == XK_Tab || keysym == XK_KP_Tab)
+ && (ev->xkey.state & ShiftMask))
+ || keysym == XK_ISO_Left_Tab)
+ {
+ if (cb) cb->entryCallback(this, TXEntryCallback::PREV_FOCUS,
+ ev->xkey.time);
+ }
+ }
+ }
+ }
+ GC gc;
+ enum { maxLen = 256 };
+ char text[maxLen];
+ TXEntryCallback* cb;
+ bool passwd;
+ bool disabled_;
+ bool gotFocus;
+};
+
+#endif
diff --git a/unix/tx/TXImage.cxx b/unix/tx/TXImage.cxx
new file mode 100644
index 00000000..5fa68288
--- /dev/null
+++ b/unix/tx/TXImage.cxx
@@ -0,0 +1,353 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXImage.cxx
+//
+
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <list>
+#include <rfb/TransImageGetter.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include "TXWindow.h"
+#include "TXImage.h"
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("TXImage");
+
+TXImage::TXImage(Display* d, int width, int height, Visual* vis_, int depth_)
+ : xim(0), dpy(d), vis(vis_), depth(depth_), tig(0), cube(0)
+{
+#ifdef HAVE_MITSHM
+ shminfo = 0;
+#endif
+ width_ = width;
+ height_ = height;
+ for (int i = 0; i < 256; i++)
+ colourMap[i].r = colourMap[i].g = colourMap[i].b = 0;
+
+ if (!vis)
+ vis = DefaultVisual(dpy,DefaultScreen(dpy));
+ if (!depth)
+ depth = DefaultDepth(dpy,DefaultScreen(dpy));
+
+ createXImage();
+ getNativePixelFormat(vis, depth);
+ colourmap = this;
+ format.bpp = 0; // just make it different to any valid format, so that...
+ setPF(nativePF); // ...setPF() always works
+}
+
+TXImage::~TXImage()
+{
+ if (data != (rdr::U8*)xim->data) delete [] data;
+ destroyXImage();
+ delete tig;
+ delete cube;
+}
+
+void TXImage::resize(int w, int h)
+{
+ if (w == width() && h == height()) return;
+
+ int oldStrideBytes = getStride() * (format.bpp/8);
+ int rowsToCopy = __rfbmin(h, height());
+ int bytesPerRow = __rfbmin(w, width()) * (format.bpp/8);
+ rdr::U8* oldData = 0;
+ bool allocData = false;
+
+ if (data != (rdr::U8*)xim->data) {
+ oldData = (rdr::U8*)data;
+ allocData = true;
+ } else {
+ oldData = new rdr::U8[xim->bytes_per_line * height()];
+ memcpy(oldData, xim->data, xim->bytes_per_line * height());
+ }
+
+ destroyXImage();
+ width_ = w;
+ height_ = h;
+ createXImage();
+
+ if (allocData)
+ data = new rdr::U8[width() * height() * (format.bpp/8)];
+ else
+ data = (rdr::U8*)xim->data;
+
+ int newStrideBytes = getStride() * (format.bpp/8);
+ for (int i = 0; i < rowsToCopy; i++)
+ memcpy((rdr::U8*)data + newStrideBytes * i, oldData + oldStrideBytes * i,
+ bytesPerRow);
+ delete [] oldData;
+}
+
+void TXImage::setPF(const PixelFormat& newPF)
+{
+ if (newPF.equal(format)) return;
+ format = newPF;
+
+ if (data != (rdr::U8*)xim->data) delete [] data;
+ delete tig;
+ tig = 0;
+
+ if (format.equal(nativePF) && format.trueColour) {
+ data = (rdr::U8*)xim->data;
+ } else {
+ data = new rdr::U8[width() * height() * (format.bpp/8)];
+ tig = new TransImageGetter();
+ tig->init(this, nativePF, 0, cube);
+ }
+}
+
+int TXImage::getStride() const
+{
+ if (data == (rdr::U8*)xim->data)
+ return xim->bytes_per_line / (xim->bits_per_pixel / 8);
+ else
+ return width();
+}
+
+void TXImage::put(Window win, GC gc, const rfb::Rect& r)
+{
+ if (r.is_empty()) return;
+ int x = r.tl.x;
+ int y = r.tl.y;
+ int w = r.width();
+ int h = r.height();
+ if (data != (rdr::U8*)xim->data) {
+ rdr::U8* ximDataStart = ((rdr::U8*)xim->data + y * xim->bytes_per_line
+ + x * (xim->bits_per_pixel / 8));
+ tig->getImage(ximDataStart, r,
+ xim->bytes_per_line / (xim->bits_per_pixel / 8));
+ }
+#ifdef HAVE_MITSHM
+ if (usingShm()) {
+ XShmPutImage(dpy, win, gc, xim, x, y, x, y, w, h, False);
+ return;
+ }
+#endif
+ XPutImage(dpy, win, gc, xim, x, y, x, y, w, h);
+}
+
+void TXImage::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
+{
+ for (int i = 0; i < nColours; i++) {
+ colourMap[firstColour+i].r = rgbs[i*3];
+ colourMap[firstColour+i].g = rgbs[i*3+1];
+ colourMap[firstColour+i].b = rgbs[i*3+2];
+ }
+}
+
+void TXImage::updateColourMap()
+{
+ tig->setColourMapEntries(0, 0, 0);
+}
+
+void TXImage::lookup(int index, int* r, int* g, int* b)
+{
+ *r = colourMap[index].r;
+ *g = colourMap[index].g;
+ *b = colourMap[index].b;
+}
+
+#ifdef HAVE_MITSHM
+static bool caughtError = false;
+
+static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
+{
+ caughtError = true;
+ return 0;
+}
+
+class TXImageCleanup {
+public:
+ std::list<TXImage*> images;
+ ~TXImageCleanup() {
+ while (!images.empty())
+ delete images.front();
+ }
+};
+
+static TXImageCleanup imageCleanup;
+#endif
+
+void TXImage::createXImage()
+{
+#ifdef HAVE_MITSHM
+ int major, minor;
+ Bool pixmaps;
+
+ if (XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
+ shminfo = new XShmSegmentInfo;
+
+ xim = XShmCreateImage(dpy, vis, depth, ZPixmap,
+ 0, shminfo, width(), height());
+
+ if (xim) {
+ shminfo->shmid = shmget(IPC_PRIVATE,
+ xim->bytes_per_line * xim->height,
+ IPC_CREAT|0777);
+
+ if (shminfo->shmid != -1) {
+ shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
+
+ if (shminfo->shmaddr != (char *)-1) {
+
+ shminfo->readOnly = False;
+
+ XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler);
+ XShmAttach(dpy, shminfo);
+ XSync(dpy, False);
+ XSetErrorHandler(oldHdlr);
+
+ if (!caughtError) {
+ vlog.debug("Using shared memory XImage");
+ imageCleanup.images.push_back(this);
+ return;
+ }
+
+ shmdt(shminfo->shmaddr);
+ } else {
+ vlog.error("shmat failed");
+ perror("shmat");
+ }
+
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ } else {
+ vlog.error("shmget failed");
+ perror("shmget");
+ }
+
+ XDestroyImage(xim);
+ xim = 0;
+ } else {
+ vlog.error("XShmCreateImage failed");
+ }
+
+ delete shminfo;
+ shminfo = 0;
+ }
+#endif
+
+ xim = XCreateImage(dpy, vis, depth, ZPixmap,
+ 0, 0, width(), height(), BitmapPad(dpy), 0);
+
+ xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
+ if (!xim->data) {
+ vlog.error("malloc failed");
+ exit(1);
+ }
+}
+
+void TXImage::destroyXImage()
+{
+#ifdef HAVE_MITSHM
+ if (shminfo) {
+ vlog.debug("Freeing shared memory XImage");
+ shmdt(shminfo->shmaddr);
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ delete shminfo;
+ shminfo = 0;
+ imageCleanup.images.remove(this);
+ }
+#endif
+ // XDestroyImage() will free(xim->data) if appropriate
+ if (xim) XDestroyImage(xim);
+ xim = 0;
+}
+
+
+static bool supportedBPP(int bpp) {
+ return (bpp == 8 || bpp == 16 || bpp == 32);
+}
+
+static int depth2bpp(Display* dpy, int depth)
+{
+ int nformats;
+ XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats);
+
+ int i;
+ for (i = 0; i < nformats; i++)
+ if (format[i].depth == depth) break;
+
+ if (i == nformats || !supportedBPP(format[i].bits_per_pixel))
+ throw rfb::Exception("Error: couldn't find suitable pixmap format");
+
+ int bpp = format[i].bits_per_pixel;
+ XFree(format);
+ return bpp;
+}
+
+void TXImage::getNativePixelFormat(Visual* vis, int depth)
+{
+ cube = 0;
+ nativePF.depth = depth;
+ nativePF.bpp = depth2bpp(dpy, depth);
+ nativePF.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
+ nativePF.trueColour = (vis->c_class == TrueColor);
+
+ vlog.info("Using default colormap and visual, %sdepth %d.",
+ (vis->c_class == TrueColor) ? "TrueColor, " :
+ ((vis->c_class == PseudoColor) ? "PseudoColor, " : ""),
+ depth);
+
+ if (nativePF.trueColour) {
+
+ nativePF.redShift = ffs(vis->red_mask) - 1;
+ nativePF.greenShift = ffs(vis->green_mask) - 1;
+ nativePF.blueShift = ffs(vis->blue_mask) - 1;
+ nativePF.redMax = vis->red_mask >> nativePF.redShift;
+ nativePF.greenMax = vis->green_mask >> nativePF.greenShift;
+ nativePF.blueMax = vis->blue_mask >> nativePF.blueShift;
+
+ } else {
+
+ XColor xc[256];
+ cube = new rfb::ColourCube(6,6,6);
+ int r;
+ for (r = 0; r < cube->nRed; r++) {
+ for (int g = 0; g < cube->nGreen; g++) {
+ for (int b = 0; b < cube->nBlue; b++) {
+ int i = (r * cube->nGreen + g) * cube->nBlue + b;
+ xc[i].red = r * 65535 / (cube->nRed-1);
+ xc[i].green = g * 65535 / (cube->nGreen-1);
+ xc[i].blue = b * 65535 / (cube->nBlue-1);
+ }
+ }
+ }
+
+ TXWindow::getColours(dpy, xc, cube->size());
+
+ for (r = 0; r < cube->nRed; r++) {
+ for (int g = 0; g < cube->nGreen; g++) {
+ for (int b = 0; b < cube->nBlue; b++) {
+ int i = (r * cube->nGreen + g) * cube->nBlue + b;
+ cube->set(r, g, b, xc[i].pixel);
+ }
+ }
+ }
+ }
+}
diff --git a/unix/tx/TXImage.h b/unix/tx/TXImage.h
new file mode 100644
index 00000000..055bd22d
--- /dev/null
+++ b/unix/tx/TXImage.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXImage.h
+//
+// A TXImage represents a rectangular off-screen image in any RFB pixel format.
+// By default it will use the "native" pixel format for the screen, which will
+// be an 8-bit colourmap unless the X display is TrueColor. The pixel format
+// can be changed via the setPF() method. The pixel data is accessible via the
+// data member inherited from FullFramePixelBuffer, or can be set via the
+// fillRect(), imageRect(), copyRect() and maskRect() methods, also inherited
+// from PixelBuffer. A rectangle of the image can be drawn into an X Window
+// via the put() method. If using a colourmap, the setColourMapEntries() and
+// updateColourMap() methods must be called to set up the colourmap as
+// appropriate.
+
+
+#ifndef __TXIMAGE_H__
+#define __TXIMAGE_H__
+
+#include <X11/Xlib.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourMap.h>
+#include <rfb/ColourCube.h>
+#ifdef HAVE_MITSHM
+#include <X11/extensions/XShm.h>
+#endif
+
+namespace rfb { class TransImageGetter; }
+
+class TXImage : public rfb::FullFramePixelBuffer, public rfb::ColourMap {
+public:
+ TXImage(Display* dpy, int width, int height, Visual* vis=0, int depth=0);
+ ~TXImage();
+
+ // resize() resizes the image, preserving the image data where possible.
+ void resize(int w, int h);
+
+ // put causes the given rectangle to be drawn onto the given window.
+ void put(Window win, GC gc, const rfb::Rect& r);
+
+ // setColourMapEntries() changes some of the entries in the colourmap.
+ // However these settings won't take effect until updateColourMap() is
+ // called. This is because recalculating the internal translation table can
+ // be expensive.
+ void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+ void updateColourMap();
+
+#ifdef HAVE_MITSHM
+ bool usingShm() { return shminfo; }
+#else
+ bool usingShm() { return 0; }
+#endif
+
+ // PixelBuffer methods
+ // width(), height(), getPF() etc are inherited from PixelBuffer
+ virtual void setPF(const rfb::PixelFormat& pf);
+ virtual int getStride() const;
+
+private:
+
+ // ColourMap method
+ virtual void lookup(int index, int* r, int* g, int* b);
+
+ void createXImage();
+ void destroyXImage();
+ void getNativePixelFormat(Visual* vis, int depth);
+
+ XImage* xim;
+ Display* dpy;
+ Visual* vis;
+ int depth;
+#ifdef HAVE_MITSHM
+ XShmSegmentInfo* shminfo;
+#endif
+ rfb::TransImageGetter* tig;
+ rfb::Colour colourMap[256];
+ rfb::PixelFormat nativePF;
+ rfb::ColourCube* cube;
+};
+
+#endif
diff --git a/unix/tx/TXLabel.h b/unix/tx/TXLabel.h
new file mode 100644
index 00000000..3d5200d6
--- /dev/null
+++ b/unix/tx/TXLabel.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXLabel.h
+//
+// An TXLabel allows you to put up multiline text in a window with various
+// alignments. The label must be big enough to contain the text - if not then
+// it will be resized appropriately.
+//
+
+#ifndef __TXLABEL_H__
+#define __TXLABEL_H__
+
+#include <stdlib.h>
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+class TXLabel : public TXWindow, public TXEventHandler {
+public:
+ enum HAlign { left, centre, right };
+ enum VAlign { top, middle, bottom };
+
+ TXLabel(Display* dpy_, const char* text_, TXWindow* parent_=0,
+ int w=1, int h=1, HAlign ha=centre, VAlign va=middle)
+ : TXWindow(dpy_, w, h, parent_), lineSpacing(2), lines(0),
+ halign(ha), valign(va)
+ {
+ setEventHandler(this);
+ setText(text_);
+ addEventMask(ExposureMask);
+ }
+
+ // setText() changes the text in the label.
+ void setText(const char* text_) {
+ text.buf = rfb::strDup(text_);
+ lines = 0;
+ int lineStart = 0;
+ int textWidth = 0;
+ int i = -1;
+ do {
+ i++;
+ if (text.buf[i] == '\n' || text.buf[i] == 0) {
+ int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+ if (tw > textWidth) textWidth = tw;
+ lineStart = i+1;
+ lines++;
+ }
+ } while (text.buf[i] != 0);
+ int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+ * lines);
+ int newWidth = __rfbmax(width(), textWidth + xPad*2);
+ int newHeight = __rfbmax(height(), textHeight + yPad*2);
+ if (width() < newWidth || height() < newHeight) {
+ resize(newWidth, newHeight);
+ }
+ invalidate();
+ }
+
+private:
+ int xOffset(int textWidth) {
+ switch (halign) {
+ case left: return xPad;
+ case right: return width() - xPad - textWidth;
+ default: return (width() - textWidth) / 2;
+ }
+ }
+
+ int yOffset(int lineNum) {
+ int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+ * lines);
+ int lineOffset = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+ * lineNum + defaultFS->ascent);
+ switch (valign) {
+ case top: return yPad + lineOffset;
+ case bottom: return height() - yPad - textHeight + lineOffset;
+ default: return (height() - textHeight) / 2 + lineOffset;
+ }
+ }
+
+ void paint() {
+ int lineNum = 0;
+ int lineStart = 0;
+ int i = -1;
+ do {
+ i++;
+ if (text.buf[i] == '\n' || text.buf[i] == 0) {
+ int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+ XDrawString(dpy, win(), defaultGC, xOffset(tw), yOffset(lineNum),
+ &text.buf[lineStart], i-lineStart);
+ lineStart = i+1;
+ lineNum++;
+ }
+ } while (text.buf[i] != 0);
+ }
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev) {
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+ }
+ }
+
+ int lineSpacing;
+ rfb::CharArray text;
+ int lines;
+ HAlign halign;
+ VAlign valign;
+};
+
+#endif
diff --git a/unix/tx/TXMenu.cxx b/unix/tx/TXMenu.cxx
new file mode 100644
index 00000000..92712f55
--- /dev/null
+++ b/unix/tx/TXMenu.cxx
@@ -0,0 +1,186 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXMenu.cxx
+//
+
+#include "TXMenu.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <rfb/util.h>
+#include <X11/keysym.h>
+
+TXMenu::TXMenu(Display* dpy_, TXMenuCallback* cb_, int w, int h,
+ TXWindow* parent_)
+ : TXWindow(dpy_, w, h, parent_), cb(cb_), nEntries(0),
+ highlight(-1)
+{
+ setEventHandler(this);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | EnterWindowMask | LeaveWindowMask);
+}
+
+TXMenu::~TXMenu()
+{
+ XFreeGC(dpy, gc);
+ for (int i = 0; i < nEntries; i++)
+ delete [] text[i];
+}
+
+inline int TXMenu::entryHeight(int i)
+{
+ if (text[i])
+ return defaultFS->ascent + defaultFS->descent + bevel*2 + yPad*2;
+ else
+ return yPad*2 + 1;
+}
+
+void TXMenu::addEntry(const char* text_, long id_)
+{
+ assert(nEntries < maxEntries);
+ text[nEntries] = rfb::strDup(text_);
+ checked[nEntries] = false;
+ id[nEntries++] = id_;
+ int tw = 0;
+ if (text_)
+ tw = XTextWidth(defaultFS, text_, strlen(text_));
+ int newWidth = width();
+ if (tw + bevel*2 + xPad*5 + tickSize > width())
+ newWidth = tw + bevel*2 + xPad*5 + tickSize;
+ int newHeight = 0;
+ for (int i = 0; i < nEntries; i++)
+ newHeight += entryHeight(i);
+ resize(newWidth, newHeight);
+}
+
+void TXMenu::check(long id_, bool checked_)
+{
+ for (int i = 0; i < nEntries; i++) {
+ if (id[i] == id_) {
+ checked[i] = checked_;
+ break;
+ }
+ }
+}
+
+void TXMenu::paint()
+{
+ int y = 0;
+ for (int i = 0; i < nEntries; i++) {
+ if (text[i]) {
+ if (i == highlight)
+ drawBevel(gc, 0, y, width(), entryHeight(i), bevel,
+ defaultBg, darkBg, lightBg);
+ else
+ XClearArea(dpy, win(), 0, y, width(), entryHeight(i), false);
+ if (checked[i])
+ XCopyPlane(dpy, tick, win(), defaultGC, 0, 0, tickSize, tickSize,
+ bevel + xPad,
+ y + bevel + yPad + defaultFS->ascent - tickSize, 1);
+
+ XDrawImageString(dpy, win(), defaultGC, bevel + xPad*2 + tickSize,
+ y + bevel + yPad + defaultFS->ascent,
+ text[i], strlen(text[i]));
+ } else {
+ XDrawLine(dpy, win(), defaultGC, bevel + xPad, y + entryHeight(i) / 2,
+ width() - bevel - xPad, y + entryHeight(i) / 2);
+ }
+ y += entryHeight(i);
+ }
+}
+
+void TXMenu::handleEvent(TXWindow* w, XEvent* ev)
+{
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+
+ case ButtonRelease:
+ {
+ int y = ev->xmotion.y;
+ int entryY = 0;
+ for (int i = 0; i < nEntries; i++) {
+ if (y >= entryY && y <= entryY + entryHeight(i)) {
+ if (cb && text[i])
+ cb->menuSelect(id[i], this);
+ break;
+ }
+ entryY += entryHeight(i);
+ }
+ highlight = -1;
+ paint();
+ break;
+ }
+
+ case ButtonPress:
+ case MotionNotify:
+ {
+ int y = ev->xmotion.y;
+ int entryY = 0;
+ for (int i = 0; i < nEntries; i++) {
+ if (y >= entryY && y <= entryY + entryHeight(i)) {
+ if (highlight != i) {
+ highlight = i;
+ paint();
+ }
+ break;
+ }
+ entryY += entryHeight(i);
+ }
+ break;
+ }
+
+ case KeyPress:
+ {
+ KeySym ks;
+ char str[256];
+ XLookupString(&ev->xkey, str, 256, &ks, NULL);
+ if (ks == XK_Escape) {
+ highlight = -1;
+ unmap();
+ } else if (ks == XK_Down || ks == XK_Up) {
+ if (nEntries < 1) break;
+ if (highlight < 0)
+ highlight = (ks == XK_Down ? nEntries-1 : 0);
+ int start = highlight;
+ int inc = (ks == XK_Down ? 1 : nEntries-1);
+ do {
+ highlight = (highlight + inc) % nEntries;
+ } while (highlight != start && !text[highlight]);
+ paint();
+ } else if (ks == XK_space || ks == XK_KP_Space ||
+ ks == XK_Return || ks == XK_KP_Enter) {
+ if (cb && highlight >= 0 && text[highlight])
+ cb->menuSelect(id[highlight], this);
+ highlight = -1;
+ paint();
+ }
+ break;
+ }
+
+ case EnterNotify:
+ case LeaveNotify:
+ highlight = -1;
+ paint();
+ break;
+ }
+}
diff --git a/unix/tx/TXMenu.h b/unix/tx/TXMenu.h
new file mode 100644
index 00000000..d0245ee9
--- /dev/null
+++ b/unix/tx/TXMenu.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXMenu.h
+//
+// A TXMenu consists of multiple entries which can be added one at a time.
+// Each entry consists of some text, and has an associated integer identifier.
+// A callback is made when a menu entry is selected.
+//
+
+#ifndef __TXMENU_H__
+#define __TXMENU_H__
+
+#include "TXWindow.h"
+
+// TXMenuCallback's menuSelect() method is called when a particular menu entry
+// is selected. The id argument identifies the menu entry.
+class TXMenu;
+class TXMenuCallback {
+public:
+ virtual void menuSelect(long id, TXMenu* menu)=0;
+};
+
+class TXMenu : public TXWindow, public TXEventHandler {
+public:
+ TXMenu(Display* dpy_, TXMenuCallback* cb=0, int width=1, int height=1,
+ TXWindow* parent_=0);
+ virtual ~TXMenu();
+
+ // addEntry() adds an entry to the end of the menu with the given text and
+ // identifier.
+ void addEntry(const char* text, long id);
+
+ // check() sets whether the given menu entry should have a tick next to it.
+ void check(long id, bool checked);
+
+private:
+ int entryHeight(int i);
+ virtual void handleEvent(TXWindow* w, XEvent* ev);
+ void paint();
+
+ GC gc;
+ TXMenuCallback* cb;
+ enum { maxEntries = 64 };
+ char* text[maxEntries];
+ long id[maxEntries];
+ bool checked[maxEntries];
+ int nEntries;
+ int highlight;
+};
+
+#endif
diff --git a/unix/tx/TXMsgBox.h b/unix/tx/TXMsgBox.h
new file mode 100644
index 00000000..ff84e6ae
--- /dev/null
+++ b/unix/tx/TXMsgBox.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXMsgBox.h
+//
+// A TXMsgBox is a specialised pop-up dialog window, designed to present
+// the user with a small amount of textual information, and potentially to
+// obtain their response.
+// TXMsgBoxes are always modal, and may have an Ok button, Ok+Cancel buttons,
+// or Yes+No buttons.
+// The MsgBox helper function creates a TXMsgBox on the fly, runs it, and
+// returns the result.
+//
+
+#ifndef __TXMSGBOX_H__
+#define __TXMSGBOX_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXButton.h"
+
+enum TXMsgBoxFlags {
+ MB_OK = 0,
+ MB_OKCANCEL = 1,
+ MB_YESNO = 4,
+ MB_ICONERROR = 0x10,
+ MB_ICONQUESTION = 0x20,
+ MB_ICONWARNING = 0x30,
+ MB_ICONINFORMATION = 0x40,
+ MB_DEFBUTTON1 = 0,
+ MB_DEFBUTTON2 = 0x100
+};
+
+class TXMsgBox : public TXDialog, public TXButtonCallback {
+public:
+ TXMsgBox(Display* dpy, const char* text, unsigned int flags, const char* title=0)
+ : TXDialog(dpy, 1, 1, "Message", true),
+ textLabel(dpy, "", this),
+ okButton(dpy, "OK", this, this, 60),
+ cancelButton(dpy, "Cancel", this, this, 60)
+ {
+ textLabel.xPad = 8;
+ textLabel.move(0, yPad*4);
+ textLabel.setText(text);
+ resize(textLabel.width(),
+ textLabel.height() + okButton.height() + yPad*12);
+
+ switch (flags & 0x30) {
+ case MB_ICONERROR:
+ toplevel("Error", this); break;
+ case MB_ICONQUESTION:
+ toplevel("Question", this); break;
+ case MB_ICONWARNING:
+ toplevel("Warning", this); break;
+ case MB_ICONINFORMATION:
+ toplevel("Information", this); break;
+ default:
+ if (title)
+ toplevel(title, this);
+ break;
+ };
+
+ switch (flags & 0x7) {
+ default:
+ okButton.move((width() - okButton.width()) / 2,
+ height() - yPad*4 - okButton.height());
+ cancelButton.unmap();
+ break;
+ case MB_OKCANCEL:
+ case MB_YESNO:
+
+ okButton.move(((width()/2) - okButton.width()) / 2,
+ height() - yPad*4 - okButton.height());
+ cancelButton.move(((width()*3/2) - cancelButton.width()) / 2,
+ height() - yPad*4 - cancelButton.height());
+ if ((flags & 0x7) == MB_YESNO) {
+ okButton.setText("Yes");
+ cancelButton.setText("No");
+ }
+ break;
+ };
+
+ setBorderWidth(1);
+ }
+
+ virtual void buttonActivate(TXButton* b) {
+ ok = (b == &okButton);
+ done = true;
+ unmap();
+ }
+
+ TXLabel textLabel;
+ TXButton okButton;
+ TXButton cancelButton;
+};
+
+#endif
diff --git a/unix/tx/TXScrollbar.cxx b/unix/tx/TXScrollbar.cxx
new file mode 100644
index 00000000..47e124c3
--- /dev/null
+++ b/unix/tx/TXScrollbar.cxx
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXScrollbar.cxx
+//
+
+#include "TXScrollbar.h"
+#include <stdio.h>
+#include <assert.h>
+
+TXScrollbar::TXScrollbar(Display* dpy_, int width, int height, bool vert,
+ TXScrollbarCallback* cb_, TXWindow* parent_)
+ : TXWindow(dpy_, width, height, parent_), cb(cb_), vertical(vert),
+ clickedInThumb(false)
+{
+ setEventHandler(this);
+ gc = XCreateGC(dpy, win(), 0, 0);
+ addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+ ButtonMotionMask);
+ setBg(scrollbarBg);
+ limit[0] = len[0] = limit[1] = len[1] = 1;
+ start[0] = start[1] = 0;
+}
+
+TXScrollbar::~TXScrollbar()
+{
+ XFreeGC(dpy, gc);
+}
+
+void TXScrollbar::set(int limit_, int start_, int len_, bool vert)
+{
+ assert(limit_ > 0 && len_ >= 0 && len_ <= limit_);
+
+ if (start_ < 0) start_ = 0;
+ if (start_ > limit_ - len_) start_ = limit_ - len_;
+
+ if (limit[vert] != limit_ || start[vert] != start_ || len[vert] != len_) {
+ limit[vert] = limit_;
+ start[vert] = start_;
+ len[vert] = len_;
+ paint();
+ }
+}
+
+void TXScrollbar::paint()
+{
+ int x = scaleToBarX(start[0]);
+ int y = scaleToBarY(start[1]);
+ int w = scaleToBarX(len[0]);
+ int h = scaleToBarY(len[1]);
+ if (y > 0) XClearArea(dpy, win(), 0, 0, 0, y, false);
+ if (x > 0) XClearArea(dpy, win(), 0, y, x, y+h, false);
+ XClearArea(dpy, win(), x+w, y, 0, y+h, false);
+ XClearArea(dpy, win(), 0, y+h, 0, 0, false);
+ drawBevel(gc, x, y, w, h, bevel, defaultBg, lightBg, darkBg);
+}
+
+void TXScrollbar::handleEvent(TXWindow* w, XEvent* ev)
+{
+ switch (ev->type) {
+ case Expose:
+ paint();
+ break;
+
+ case ButtonPress:
+ {
+ xDown = ev->xbutton.x;
+ yDown = ev->xbutton.y;
+ xStart = start[0];
+ yStart = start[1];
+ bool clickedInThumbX = false;
+ if (xDown < scaleToBarX(start[0])) {
+ set(limit[0], start[0] - len[0], len[0], false);
+ } else if (xDown >= scaleToBarX(start[0]+len[0])) {
+ set(limit[0], start[0] + len[0], len[0], false);
+ } else {
+ clickedInThumbX = true;
+ }
+ bool clickedInThumbY = false;
+ if (yDown < scaleToBarY(start[1])) {
+ set(limit[1], start[1] - len[1], len[1], true);
+ } else if (yDown >= scaleToBarY(start[1]+len[1])) {
+ set(limit[1], start[1] + len[1], len[1], true);
+ } else {
+ clickedInThumbY = true;
+ }
+ clickedInThumb = clickedInThumbX && clickedInThumbY;
+ if (cb) cb->scrollbarPos(start[0], start[1], this);
+ }
+ break;
+
+ case ButtonRelease:
+ case MotionNotify:
+ while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev));
+ if (clickedInThumb) {
+ int dx = ev->xmotion.x - xDown;
+ int dy = ev->xmotion.y - yDown;
+ set(limit[0], xStart + barToScaleX(dx), len[0], false);
+ set(limit[1], yStart + barToScaleY(dy), len[1], true);
+ if (cb) cb->scrollbarPos(start[0], start[1], this);
+ }
+ break;
+ }
+}
diff --git a/unix/tx/TXScrollbar.h b/unix/tx/TXScrollbar.h
new file mode 100644
index 00000000..87fec3d8
--- /dev/null
+++ b/unix/tx/TXScrollbar.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXScrollbar.h
+//
+// A TXScrollbar represents a range of values starting at start, of length len,
+// between zero and limit. The vertical argument to the constructor says
+// whether the scrollbar is horizontal or vertical.
+//
+// In fact it can represent a range in each dimension but usually one of the
+// dimensions is fixed, according to the vertical flag (for a vertical
+// scrollbar, the horizontal dimension is fixed, and vice-versa).
+//
+// The TXScrollbarCallback argument is an object which will be notified when
+// the user has attempted to move the scrollbar. The x and y arguments to the
+// scrollbarPos() method give the start values in the respective dimensions.
+// They are guaranteed to be between 0 and limit-len.
+//
+
+#ifndef __TXSCROLLBAR_H__
+#define __TXSCROLLBAR_H__
+
+#include "TXWindow.h"
+
+class TXScrollbarCallback;
+
+class TXScrollbar : public TXWindow, public TXEventHandler {
+public:
+ TXScrollbar(Display* dpy_, int width=1, int height=1, bool vertical=false,
+ TXScrollbarCallback* cb=0, TXWindow* parent_=0);
+ virtual ~TXScrollbar();
+
+ // set() sets the limit, start and length of the range represented by the
+ // scrollbar. The values of limit and len passed in must be valid
+ // (i.e. limit > 0 and 0 <= len <= limit). Values of start are clipped to
+ // the range 0 to limit-len.
+ void set(int limit, int start, int len) { set(limit, start, len, vertical); }
+
+ // set() with an extra argument vert can be used to represent a range in both
+ // dimensions simultaneously.
+ void set(int limit, int start, int len, bool vert);
+
+ virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+private:
+ int scaleToBarX(int x) { return (x * width() + limit[0]/2) / limit[0]; }
+ int scaleToBarY(int y) { return (y * height() + limit[1]/2) / limit[1]; }
+ int barToScaleX(int x) { return (x * limit[0] + width()/2) / width(); }
+ int barToScaleY(int y) { return (y * limit[1] + height()/2) / height(); }
+ void paint();
+
+ GC gc;
+ TXScrollbarCallback* cb;
+ int limit[2];
+ int start[2];
+ int len[2];
+ int xDown, yDown;
+ int xStart, yStart;
+ bool vertical;
+ bool clickedInThumb;
+};
+
+class TXScrollbarCallback {
+public:
+ virtual void scrollbarPos(int x, int y, TXScrollbar* sb)=0;
+};
+#endif
diff --git a/unix/tx/TXViewport.cxx b/unix/tx/TXViewport.cxx
new file mode 100644
index 00000000..abe51734
--- /dev/null
+++ b/unix/tx/TXViewport.cxx
@@ -0,0 +1,155 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXViewport.cxx
+//
+
+#include "TXViewport.h"
+#include <stdio.h>
+
+TXViewport::TXViewport(Display* dpy_, int w, int h, TXWindow* parent_)
+ : TXWindow(dpy_, w, h, parent_), child(0), hScrollbar(0),
+ vScrollbar(0), scrollbarSize(15), xOff(0), yOff(0), bumpScrollTimer(this),
+ bumpScroll(false), needScrollbars(false), bumpScrollX(0), bumpScrollY(0)
+{
+ clipper = new TXWindow(dpy, width()-scrollbarSize, height()-scrollbarSize,
+ this);
+ clipper->setBg(black);
+ hScrollbar = new TXScrollbar(dpy, width()-scrollbarSize, scrollbarSize,
+ false, this, this);
+ vScrollbar = new TXScrollbar(dpy, scrollbarSize, height()-scrollbarSize,
+ true, this, this);
+}
+
+TXViewport::~TXViewport()
+{
+ delete clipper;
+ delete hScrollbar;
+ delete vScrollbar;
+}
+
+void TXViewport::setChild(TXWindow* child_)
+{
+ child = child_;
+ XReparentWindow(dpy, child->win(), clipper->win(), 0, 0);
+ xOff = yOff = 0;
+ child->map();
+ resizeNotify();
+}
+
+bool TXViewport::setOffset(int x, int y)
+{
+ if (clipper->width() >= child->width()) {
+ x = (clipper->width() - child->width()) / 2;
+ } else {
+ if (x > 0) x = 0;
+ if (x + child->width() < clipper->width())
+ x = clipper->width() - child->width();
+ }
+
+ if (clipper->height() >= child->height()) {
+ y = (clipper->height() - child->height()) / 2;
+ } else {
+ if (y > 0) y = 0;
+ if (y + child->height() < clipper->height())
+ y = clipper->height() - child->height();
+ }
+
+ if (x != xOff || y != yOff) {
+ xOff = x;
+ yOff = y;
+ child->move(xOff, yOff);
+ return true;
+ }
+
+ return false;
+}
+
+void TXViewport::setBumpScroll(bool b)
+{
+ bumpScroll = b;
+ resizeNotify();
+}
+
+// Note: bumpScrollEvent() only works if the viewport is positioned at 0,0 and
+// is the same width and height as the screen.
+bool TXViewport::bumpScrollEvent(XMotionEvent* ev)
+{
+ if (!bumpScroll) return false;
+ int bumpScrollPixels = 20;
+ bumpScrollX = bumpScrollY = 0;
+
+ if (ev->x_root == width()-1) bumpScrollX = -bumpScrollPixels;
+ else if (ev->x_root == 0) bumpScrollX = bumpScrollPixels;
+ if (ev->y_root == height()-1) bumpScrollY = -bumpScrollPixels;
+ else if (ev->y_root == 0) bumpScrollY = bumpScrollPixels;
+
+ if (bumpScrollX || bumpScrollY) {
+ if (bumpScrollTimer.isStarted()) return true;
+ if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY)) {
+ bumpScrollTimer.start(25);
+ return true;
+ }
+ }
+
+ bumpScrollTimer.stop();
+ return false;
+}
+
+bool TXViewport::handleTimeout(rfb::Timer* timer) {
+ return setOffset(xOff + bumpScrollX, yOff + bumpScrollY);
+}
+
+void TXViewport::resizeNotify()
+{
+ needScrollbars = (!bumpScroll &&
+ (width() < child->width() || height() < child->height()) &&
+ (width() > scrollbarSize && height() > scrollbarSize));
+ if (needScrollbars) {
+ clipper->resize(width()-scrollbarSize, height()-scrollbarSize);
+ hScrollbar->map();
+ vScrollbar->map();
+ } else {
+ clipper->resize(width(), height());
+ hScrollbar->unmap();
+ vScrollbar->unmap();
+ }
+
+ setOffset(xOff, yOff);
+
+ if (needScrollbars) {
+ hScrollbar->move(0, height()-scrollbarSize);
+ hScrollbar->resize(width()-scrollbarSize, scrollbarSize);
+ hScrollbar->set(child->width(), -xOff, width()-scrollbarSize);
+ vScrollbar->move(width()-scrollbarSize, 0);
+ vScrollbar->resize(scrollbarSize, height()-scrollbarSize);
+ vScrollbar->set(child->height(), -yOff, height()-scrollbarSize);
+ }
+}
+
+void TXViewport::scrollbarPos(int x, int y, TXScrollbar* sb)
+{
+ if (sb == hScrollbar) {
+ x = -x;
+ y = yOff;
+ } else {
+ x = xOff;
+ y = -y;
+ }
+ setOffset(x, y);
+}
diff --git a/unix/tx/TXViewport.h b/unix/tx/TXViewport.h
new file mode 100644
index 00000000..0c9857d2
--- /dev/null
+++ b/unix/tx/TXViewport.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXViewport.h
+//
+// A TXViewport allows a large window to be viewed by adding scrollbars to the
+// right and bottom if necessary. It also has a bump-scroll mode where there
+// are no scrollbars, and scrolling is achieved by bumping up against the edge
+// of the screen instead. Note that this only works when the viewport fills
+// the entire screen. If the child window is smaller than the viewport, it is
+// always positioned centrally in the viewport.
+
+#ifndef __TXVIEWPORT_H__
+#define __TXVIEWPORT_H__
+
+#include <rfb/Timer.h>
+#include "TXWindow.h"
+#include "TXScrollbar.h"
+
+class TXViewport : public TXWindow, public TXScrollbarCallback,
+ public rfb::Timer::Callback {
+public:
+ TXViewport(Display* dpy_, int width, int height, TXWindow* parent_=0);
+ virtual ~TXViewport();
+
+ // setChild() sets the child window which is to be viewed in the viewport.
+ void setChild(TXWindow* child_);
+
+ // setOffset() sets the position of the child in the viewport. Note that the
+ // offsets are negative. For example when the offset is (-100,-30), position
+ // (100,30) in the child window is at the top-left of the viewport. The
+ // offsets given are clipped to keep the child window filling the viewport
+ // (except where the child window is smaller than the viewport, in which case
+ // it is always positioned centrally in the viewport). It returns true if
+ // the child was repositioned.
+ bool setOffset(int x, int y);
+
+ // setBumpScroll() puts the viewport in bump-scroll mode.
+ void setBumpScroll(bool b);
+
+ // bumpScrollEvent() can be called with a MotionNotify event which may
+ // potentially be against the edge of the screen. It returns true if the
+ // event was used for bump-scrolling, false if it should be processed
+ // normally.
+ bool bumpScrollEvent(XMotionEvent* ev);
+
+private:
+ virtual void resizeNotify();
+ virtual void scrollbarPos(int x, int y, TXScrollbar* sb);
+ virtual bool handleTimeout(rfb::Timer* timer);
+ TXWindow* clipper;
+ TXWindow* child;
+ TXScrollbar* hScrollbar;
+ TXScrollbar* vScrollbar;
+ const int scrollbarSize;
+ int xOff, yOff;
+ rfb::Timer bumpScrollTimer;
+ bool bumpScroll;
+ bool needScrollbars;
+ int bumpScrollX, bumpScrollY;
+};
+#endif
diff --git a/unix/tx/TXWindow.cxx b/unix/tx/TXWindow.cxx
new file mode 100644
index 00000000..6d211beb
--- /dev/null
+++ b/unix/tx/TXWindow.cxx
@@ -0,0 +1,486 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXWindow.cxx
+//
+
+#include <X11/Xatom.h>
+#include "TXWindow.h"
+#include <list>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rfb/util.h>
+
+std::list<TXWindow*> windows;
+
+Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+Atom xaCLIPBOARD;
+unsigned long TXWindow::black, TXWindow::white;
+unsigned long TXWindow::defaultFg, TXWindow::defaultBg;
+unsigned long TXWindow::lightBg, TXWindow::darkBg;
+unsigned long TXWindow::disabledFg, TXWindow::disabledBg;
+unsigned long TXWindow::enabledBg;
+unsigned long TXWindow::scrollbarBg;
+Colormap TXWindow::cmap = 0;
+GC TXWindow::defaultGC = 0;
+Font TXWindow::defaultFont = 0;
+XFontStruct* TXWindow::defaultFS = 0;
+Time TXWindow::cutBufferTime = 0;
+Pixmap TXWindow::dot = 0, TXWindow::tick = 0;
+const int TXWindow::dotSize = 4, TXWindow::tickSize = 8;
+char* TXWindow::defaultWindowClass;
+
+void TXWindow::init(Display* dpy, const char* defaultWindowClass_)
+{
+ cmap = DefaultColormap(dpy,DefaultScreen(dpy));
+ wmProtocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ wmTakeFocus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ xaTIMESTAMP = XInternAtom(dpy, "TIMESTAMP", False);
+ xaTARGETS = XInternAtom(dpy, "TARGETS", False);
+ xaSELECTION_TIME = XInternAtom(dpy, "SELECTION_TIME", False);
+ xaSELECTION_STRING = XInternAtom(dpy, "SELECTION_STRING", False);
+ xaCLIPBOARD = XInternAtom(dpy, "CLIPBOARD", False);
+ XColor cols[6];
+ cols[0].red = cols[0].green = cols[0].blue = 0x0000;
+ cols[1].red = cols[1].green = cols[1].blue = 0xbbbb;
+ cols[2].red = cols[2].green = cols[2].blue = 0xeeee;
+ cols[3].red = cols[3].green = cols[3].blue = 0x5555;
+ cols[4].red = cols[4].green = cols[4].blue = 0x8888;
+ cols[5].red = cols[5].green = cols[5].blue = 0xffff;
+ getColours(dpy, cols, 6);
+ black = defaultFg = cols[0].pixel;
+ defaultBg = disabledBg = cols[1].pixel;
+ lightBg = cols[2].pixel;
+ darkBg = disabledFg = cols[3].pixel;
+ scrollbarBg = cols[4].pixel;
+ white = enabledBg = cols[5].pixel;
+ defaultGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);
+ defaultFS
+ = XLoadQueryFont(dpy, "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
+ if (!defaultFS) {
+ defaultFS = XLoadQueryFont(dpy, "fixed");
+ if (!defaultFS) {
+ fprintf(stderr,"Failed to load any font\n");
+ exit(1);
+ }
+ }
+ defaultFont = defaultFS->fid;
+ XSetForeground(dpy, defaultGC, defaultFg);
+ XSetBackground(dpy, defaultGC, defaultBg);
+ XSetFont(dpy, defaultGC, defaultFont);
+ XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask);
+
+ static char dotBits[] = { 0x06, 0x0f, 0x0f, 0x06};
+ dot = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), dotBits,
+ dotSize, dotSize);
+ static char tickBits[] = { 0x80, 0xc0, 0xe2, 0x76, 0x3e, 0x1c, 0x08, 0x00};
+ tick = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), tickBits,
+ tickSize, tickSize);
+ defaultWindowClass = rfb::strDup(defaultWindowClass_);
+}
+
+void TXWindow::handleXEvents(Display* dpy)
+{
+ while (XPending(dpy)) {
+ XEvent ev;
+ XNextEvent(dpy, &ev);
+ if (ev.type == MappingNotify) {
+ XRefreshKeyboardMapping(&ev.xmapping);
+ } else if (ev.type == PropertyNotify &&
+ ev.xproperty.window == DefaultRootWindow(dpy) &&
+ ev.xproperty.atom == XA_CUT_BUFFER0) {
+ cutBufferTime = ev.xproperty.time;
+ } else {
+ std::list<TXWindow*>::iterator i;
+ for (i = windows.begin(); i != windows.end(); i++) {
+ if ((*i)->win() == ev.xany.window)
+ (*i)->handleXEvent(&ev);
+ }
+ }
+ }
+}
+
+void TXWindow::getColours(Display* dpy, XColor* cols, int nCols)
+{
+ bool* got = new bool[nCols];
+ bool failed = false;
+ int i;
+ for (i = 0; i < nCols; i++) {
+ if (XAllocColor(dpy, cmap, &cols[i])) {
+ got[i] = true;
+ } else {
+ got[i] = false;
+ failed = true;
+ }
+ }
+
+ if (!failed) {
+ delete [] got;
+ return;
+ }
+
+ // AllocColor has failed. This is because the colormap is full. So the
+ // only thing we can do is use the "shared" pixels in the colormap. The
+ // code below is designed to work even when the colormap isn't full so is
+ // more complex than it needs to be in this case. However it would be
+ // useful one day to be able to restrict the number of colours allocated by
+ // an application so I'm leaving it in here.
+
+ // For each pixel in the colormap, try to allocate exactly its RGB values.
+ // If this returns a different pixel then it must be a private or
+ // unallocated pixel, so we can't use it. If it returns us the same pixel
+ // again, it's almost certainly a shared colour, so we can use it (actually
+ // it is possible that it was an unallocated pixel which we've now
+ // allocated - by going through the pixels in reverse order we make this
+ // unlikely except for the lowest unallocated pixel - this works because of
+ // the way the X server allocates new pixels).
+
+ int cmapSize = DisplayCells(dpy,DefaultScreen(dpy));
+
+ XColor* cm = new XColor[cmapSize];
+ bool* shared = new bool[cmapSize];
+ bool* usedAsNearest = new bool[cmapSize];
+
+ for (i = 0; i < cmapSize; i++) {
+ cm[i].pixel = i;
+ shared[i] = usedAsNearest[i] = false;
+ }
+
+ XQueryColors(dpy, cmap, cm, cmapSize);
+
+ for (i = cmapSize-1; i >= 0; i--) {
+ if (XAllocColor(dpy, cmap, &cm[i])) {
+ if (cm[i].pixel == (unsigned long)i) {
+ shared[i] = true;
+ } else {
+ XFreeColors(dpy, cmap, &cm[i].pixel, 1, 0);
+ }
+ }
+ }
+
+ for (int j = 0; j < nCols; j++) {
+ unsigned long minDistance = ULONG_MAX;
+ unsigned long nearestPixel = 0;
+ if (!got[j]) {
+ for (i = 0; i < cmapSize; i++) {
+ if (shared[i]) {
+ unsigned long rd = (cm[i].red - cols[j].red)/2;
+ unsigned long gd = (cm[i].green - cols[j].green)/2;
+ unsigned long bd = (cm[i].blue - cols[j].blue)/2;
+ unsigned long distance = (rd*rd + gd*gd + bd*bd);
+
+ if (distance < minDistance) {
+ minDistance = distance;
+ nearestPixel = i;
+ }
+ }
+ }
+
+ cols[j].pixel = nearestPixel;
+ usedAsNearest[nearestPixel] = true;
+ }
+ }
+
+ for (i = 0; i < cmapSize; i++) {
+ if (shared[i] && !usedAsNearest[i]) {
+ unsigned long p = i;
+ XFreeColors(dpy, cmap, &p, 1, 0);
+ }
+ }
+}
+
+Window TXWindow::windowWithName(Display* dpy, Window top, const char* name)
+{
+ char* windowName;
+ if (XFetchName(dpy, top, &windowName)) {
+ if (strcmp(windowName, name) == 0) {
+ XFree(windowName);
+ return top;
+ }
+ XFree(windowName);
+ }
+
+ Window* children;
+ Window dummy;
+ unsigned int nchildren;
+ if (!XQueryTree(dpy, top, &dummy, &dummy, &children,&nchildren) || !children)
+ return 0;
+
+ for (int i = 0; i < (int)nchildren; i++) {
+ Window w = windowWithName(dpy, children[i], name);
+ if (w) {
+ XFree((char*)children);
+ return w;
+ }
+ }
+ XFree((char*)children);
+ return 0;
+}
+
+
+TXWindow::TXWindow(Display* dpy_, int w, int h, TXWindow* parent_,
+ int borderWidth)
+ : dpy(dpy_), xPad(3), yPad(3), bevel(2), parent(parent_), width_(w),
+ height_(h), eventHandler(0), dwc(0), eventMask(0), toplevel_(false)
+{
+ sizeHints.flags = 0;
+ XSetWindowAttributes attr;
+ attr.background_pixel = defaultBg;
+ attr.border_pixel = 0;
+ Window par = parent ? parent->win() : DefaultRootWindow(dpy);
+ win_ = XCreateWindow(dpy, par, 0, 0, width_, height_, borderWidth,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWBackPixel | CWBorderPixel, &attr);
+ if (parent) map();
+
+ windows.push_back(this);
+}
+
+TXWindow::~TXWindow()
+{
+ windows.remove(this);
+ XDestroyWindow(dpy, win());
+}
+
+void TXWindow::toplevel(const char* name, TXDeleteWindowCallback* dwc_,
+ int argc, char** argv, const char* windowClass,
+ bool iconic)
+{
+ toplevel_ = true;
+ XWMHints wmHints;
+ wmHints.flags = InputHint|StateHint;
+ wmHints.input = True;
+ wmHints.initial_state = iconic ? IconicState : NormalState;
+ XClassHint classHint;
+ if (!windowClass) windowClass = defaultWindowClass;
+ classHint.res_name = (char*)name;
+ classHint.res_class = (char*)windowClass;
+ XSetWMProperties(dpy, win(), 0, 0, argv, argc,
+ &sizeHints, &wmHints, &classHint);
+ XStoreName(dpy, win(), name);
+ XSetIconName(dpy, win(), name);
+ Atom protocols[10];
+ int nProtocols = 0;
+ protocols[nProtocols++] = wmTakeFocus;
+ dwc = dwc_;
+ if (dwc)
+ protocols[nProtocols++] = wmDeleteWindow;
+ XSetWMProtocols(dpy, win(), protocols, nProtocols);
+ addEventMask(StructureNotifyMask);
+}
+
+void TXWindow::setMaxSize(int w, int h)
+{
+ sizeHints.flags |= PMaxSize;
+ sizeHints.max_width = w;
+ sizeHints.max_height = h;
+ XSetWMNormalHints(dpy, win(), &sizeHints);
+}
+
+void TXWindow::setUSPosition(int x, int y)
+{
+ sizeHints.flags |= USPosition;
+ sizeHints.x = x;
+ sizeHints.y = y;
+ XSetWMNormalHints(dpy, win(), &sizeHints);
+ move(x, y);
+}
+
+void TXWindow::setGeometry(const char* geom, int x, int y, int w, int h)
+{
+ char defGeom[256];
+ sprintf(defGeom,"%dx%d+%d+%d",w,h,x,y);
+ XWMGeometry(dpy, DefaultScreen(dpy), strEmptyToNull((char*)geom), defGeom,
+ 0, &sizeHints, &x, &y, &w, &h, &sizeHints.win_gravity);
+ sizeHints.flags |= PWinGravity;
+ setUSPosition(x, y);
+ resize(w, h);
+}
+
+TXEventHandler* TXWindow::setEventHandler(TXEventHandler* h)
+{
+ TXEventHandler* old = eventHandler;
+ eventHandler = h;
+ return old;
+}
+
+void TXWindow::addEventMask(long mask)
+{
+ eventMask |= mask;
+ XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::removeEventMask(long mask)
+{
+ eventMask &= ~mask;
+ XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::unmap()
+{
+ XUnmapWindow(dpy, win());
+ if (toplevel_) {
+ XUnmapEvent ue;
+ ue.type = UnmapNotify;
+ ue.display = dpy;
+ ue.event = DefaultRootWindow(dpy);
+ ue.window = win();
+ ue.from_configure = False;
+ XSendEvent(dpy, DefaultRootWindow(dpy), False,
+ (SubstructureRedirectMask|SubstructureNotifyMask),
+ (XEvent*)&ue);
+ }
+}
+
+void TXWindow::resize(int w, int h)
+{
+ //if (w == width_ && h == height_) return;
+ XResizeWindow(dpy, win(), w, h);
+ width_ = w;
+ height_ = h;
+ resizeNotify();
+}
+
+void TXWindow::setBorderWidth(int bw)
+{
+ XWindowChanges c;
+ c.border_width = bw;
+ XConfigureWindow(dpy, win(), CWBorderWidth, &c);
+}
+
+void TXWindow::ownSelection(Atom selection, Time time)
+{
+ XSetSelectionOwner(dpy, selection, win(), time);
+ if (XGetSelectionOwner(dpy, selection) == win()) {
+ selectionOwner_[selection] = true;
+ selectionOwnTime[selection] = time;
+ }
+}
+
+void TXWindow::handleXEvent(XEvent* ev)
+{
+ switch (ev->type) {
+
+ case ClientMessage:
+ if (ev->xclient.message_type == wmProtocols) {
+ if ((Atom)ev->xclient.data.l[0] == wmDeleteWindow) {
+ if (dwc) dwc->deleteWindow(this);
+ } else if ((Atom)ev->xclient.data.l[0] == wmTakeFocus) {
+ takeFocus(ev->xclient.data.l[1]);
+ }
+ }
+ break;
+
+ case ConfigureNotify:
+ if (ev->xconfigure.width != width_ || ev->xconfigure.height != height_) {
+ width_ = ev->xconfigure.width;
+ height_ = ev->xconfigure.height;
+ resizeNotify();
+ }
+ break;
+
+ case SelectionNotify:
+ if (ev->xselection.property != None) {
+ Atom type;
+ int format;
+ unsigned long nitems, after;
+ unsigned char *data;
+ XGetWindowProperty(dpy, win(), ev->xselection.property, 0, 16384, True,
+ AnyPropertyType, &type, &format,
+ &nitems, &after, &data);
+ if (type != None) {
+ selectionNotify(&ev->xselection, type, format, nitems, data);
+ XFree(data);
+ break;
+ }
+ }
+ selectionNotify(&ev->xselection, 0, 0, 0, 0);
+ break;
+
+ case SelectionRequest:
+ {
+ XSelectionEvent se;
+ se.type = SelectionNotify;
+ se.display = ev->xselectionrequest.display;
+ se.requestor = ev->xselectionrequest.requestor;
+ se.selection = ev->xselectionrequest.selection;
+ se.time = ev->xselectionrequest.time;
+ se.target = ev->xselectionrequest.target;
+ if (ev->xselectionrequest.property == None)
+ ev->xselectionrequest.property = ev->xselectionrequest.target;
+ if (!selectionOwner_[se.selection]) {
+ se.property = None;
+ } else {
+ se.property = ev->xselectionrequest.property;
+ if (se.target == xaTARGETS) {
+ Atom targets[2];
+ targets[0] = xaTIMESTAMP;
+ targets[1] = XA_STRING;
+ XChangeProperty(dpy, se.requestor, se.property, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)targets, 2);
+ } else if (se.target == xaTIMESTAMP) {
+ rdr::U32 t = selectionOwnTime[se.selection];
+ XChangeProperty(dpy, se.requestor, se.property, XA_INTEGER, 32,
+ PropModeReplace, (unsigned char*)&t, 1);
+ } else if (se.target == XA_STRING) {
+ if (!selectionRequest(se.requestor, se.selection, se.property))
+ se.property = None;
+ }
+ }
+ XSendEvent(dpy, se.requestor, False, 0, (XEvent*)&se);
+ break;
+ }
+
+ case SelectionClear:
+ selectionOwner_[ev->xselectionclear.selection] = false;
+ break;
+ }
+
+ if (eventHandler) eventHandler->handleEvent(this, ev);
+}
+
+void TXWindow::drawBevel(GC gc, int x, int y, int w, int h, int b,
+ unsigned long middle, unsigned long tl,
+ unsigned long br, bool round)
+{
+ if (round) {
+ XGCValues gcv;
+ gcv.line_width = b;
+ XChangeGC(dpy, gc, GCLineWidth, &gcv);
+ XSetForeground(dpy, gc, middle);
+ XFillArc(dpy, win(), gc, x, y, w-b/2, h-b/2, 0, 360*64);
+ XSetForeground(dpy, gc, tl);
+ XDrawArc(dpy, win(), gc, x, y, w-b/2, h-b/2, 45*64, 180*64);
+ XSetForeground(dpy, gc, br);
+ XDrawArc(dpy, win(), gc, x, y, w-b/2, h-b/2, 225*64, 180*64);
+ } else {
+ XSetForeground(dpy, gc, middle);
+ if (w-2*b > 0 && h-2*b > 0)
+ XFillRectangle(dpy, win(), gc, x+b, y+b, w-2*b, h-2*b);
+ XSetForeground(dpy, gc, tl);
+ XFillRectangle(dpy, win(), gc, x, y, w, b);
+ XFillRectangle(dpy, win(), gc, x, y, b, h);
+ XSetForeground(dpy, gc, br);
+ for (int i = 0; i < b; i++) {
+ if (w-i > 0) XFillRectangle(dpy, win(), gc, x+i, y+h-1-i, w-i, 1);
+ if (h-1-i > 0) XFillRectangle(dpy, win(), gc, x+w-1-i, y+i+1, 1, h-1-i);
+ }
+ }
+}
diff --git a/unix/tx/TXWindow.h b/unix/tx/TXWindow.h
new file mode 100644
index 00000000..5ada181b
--- /dev/null
+++ b/unix/tx/TXWindow.h
@@ -0,0 +1,211 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXWindow.h
+//
+// A TXWindow is the base class for all tx windows (widgets). In addition it
+// contains a number of static methods and members which are used throughout
+// tx.
+//
+// Before calling any other tx methods, TXWindow::init() must be called with
+// the X display to use.
+
+#ifndef __TXWINDOW_H__
+#define __TXWINDOW_H__
+
+#include <rdr/types.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <map>
+
+
+// TXDeleteWindowCallback's deleteWindow() method is called when a top-level
+// window is "deleted" (closed) by the user using the window manager.
+class TXWindow;
+class TXDeleteWindowCallback {
+public:
+ virtual void deleteWindow(TXWindow* w) = 0;
+};
+
+// TXEventHandler is an interface implemented by classes wanting to handle X
+// events on a window. Most derived classes of window are their own event
+// handlers.
+class TXEventHandler {
+public:
+ virtual void handleEvent(TXWindow* w, XEvent* ev) = 0;
+};
+
+class TXWindow {
+public:
+
+ // Constructor - creates a window of the given size, with the default
+ // background (currently grey). It is mapped by default if it has a parent.
+ // If no parent is specified its parent is the root window and it will remain
+ // unmapped.
+ TXWindow(Display* dpy_, int width=1, int height=1, TXWindow* parent_=0,
+ int borderWidth=0);
+ virtual ~TXWindow();
+
+ // toplevel() declares that this is a top-level window. Various
+ // window-manager-related properties are set on the window. The given
+ // TXDeleteWindowCallback is notified when the window is "deleted" (cloesd)
+ // by the user.
+ void toplevel(const char* name, TXDeleteWindowCallback* dwc=0,
+ int argc=0, char** argv=0, const char* windowClass=0,
+ bool iconic=false);
+
+ // setMaxSize() tells the window manager the maximum size to allow a
+ // top-level window. It has no effect on a non-top-level window.
+ void setMaxSize(int w, int h);
+
+ // setUSPosition() tells the window manager the position which the "user" has
+ // asked for a top-level window. Most window managers ignore requests by a
+ // program for position, so you have to tell it that the "user" asked for the
+ // position. This has no effect on a non-top-level window.
+ void setUSPosition(int x, int y);
+
+ void setGeometry(const char* geom, int x, int y, int w, int h);
+
+ // setTransientFor() tells the window manager that this window is "owned" by
+ // the given window. The window manager can use this information as it sees
+ // fit.
+ void setTransientFor(Window w) { XSetTransientForHint(dpy, win(), w); }
+
+ // setEventHandler() sets the TXEventHandler to handle X events for this
+ // window. It returns the previous event handler, so that handlers can chain
+ // themselves.
+ TXEventHandler* setEventHandler(TXEventHandler* h);
+
+ // Accessor methods
+ Window win() { return win_; }
+ int width() { return width_; }
+ int height() { return height_; }
+
+ // selectionOwner() returns true if this window owns the given selection.
+ bool selectionOwner(Atom selection) { return selectionOwner_[selection]; }
+
+ // Wrappers around common Xlib calls
+ void addEventMask(long mask);
+ void removeEventMask(long mask);
+ void map() { XMapWindow(dpy, win()); }
+ void unmap();
+ void setBg(unsigned long bg) { XSetWindowBackground(dpy, win(), bg); }
+ void move(int x, int y) { XMoveWindow(dpy, win(), x, y); }
+ void resize(int w, int h);
+ void raise() { XRaiseWindow(dpy, win()); }
+ void setBorderWidth(int bw);
+ void invalidate(int x=0, int y=0, int w=0, int h=0) { XClearArea(dpy, win(), x, y, w, h, True); }
+
+ // ownSelection requests that the window owns the given selection from the
+ // given time (the time should be taken from an X event).
+ void ownSelection(Atom selection, Time time);
+
+
+ // drawBevel draws a rectangular or circular bevel filling the given
+ // rectangle, using the given colours for the middle, the top/left and the
+ // bottom/right.
+ void drawBevel(GC gc, int x, int y, int w, int h, int b,
+ unsigned long middle, unsigned long tl, unsigned long br,
+ bool round=false);
+
+ // Methods to be overridden in a derived class
+
+ // resizeNotify() is called whenever the window's dimensions may have
+ // changed.
+ virtual void resizeNotify() {}
+
+ // takeFocus() is called when the window has received keyboard focus from the
+ // window manager.
+ virtual void takeFocus(Time time) {}
+
+ // selectionNotify() is called when the selection owner has replied to a
+ // request for information about a selection from the selection owner.
+ virtual void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+ int nitems, void* data) {}
+
+ // selectionRequest() is called when this window is the selection owner and
+ // another X client has requested the selection. It should set the given
+ // property on the given window to the value of the given selection,
+ // returning true if successful, false otherwise.
+ virtual bool selectionRequest(Window requestor,
+ Atom selection, Atom property) { return false;}
+
+ // Static methods
+
+ // init() must be called before any other tx methods.
+ static void init(Display* dpy, const char* defaultWindowClass);
+
+ // getColours() sets the pixel values in the cols array to the best available
+ // for the given rgb values, even in the case of a full colormap.
+ static void getColours(Display* dpy, XColor* cols, int nCols);
+
+ // handleXEvents() should be called whenever there are events to handle on
+ // the connection to the X display. It process all available events, then
+ // returns when there are no more events to process.
+ static void handleXEvents(Display* dpy);
+
+ // windowWithName() locates a window with a given name on a display.
+ static Window windowWithName(Display* dpy, Window top, const char* name);
+
+ // strEmptyToNull() returns the string it's given but turns an empty string
+ // into null, which can be useful for passing rfb parameters to Xlib calls.
+ static char* strEmptyToNull(char* s) { return s && s[0] ? s : 0; }
+
+ // The following are default values for various things.
+ static unsigned long black, white;
+ static unsigned long defaultFg, defaultBg, lightBg, darkBg;
+ static unsigned long disabledFg, disabledBg, enabledBg;
+ static unsigned long scrollbarBg;
+ static GC defaultGC;
+ static Colormap cmap;
+ static Font defaultFont;
+ static XFontStruct* defaultFS;
+ static Time cutBufferTime;
+ static Pixmap dot, tick;
+ static const int dotSize, tickSize;
+ static char* defaultWindowClass;
+
+ Display* const dpy;
+
+ int xPad, yPad, bevel;
+
+private:
+
+ // handleXEvent() is called from handleXEvents() when an event for this
+ // window arrives. It does general event processing before calling on to the
+ // event handler.
+ void handleXEvent(XEvent* ev);
+
+ TXWindow* parent;
+ Window win_;
+ int width_, height_;
+ TXEventHandler* eventHandler;
+ TXDeleteWindowCallback* dwc;
+ long eventMask;
+ XSizeHints sizeHints;
+ std::map<Atom,Time> selectionOwnTime;
+ std::map<Atom,bool> selectionOwner_;
+ bool toplevel_;
+};
+
+extern Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+extern Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+extern Atom xaCLIPBOARD;
+
+#endif