diff options
27 files changed, 1985 insertions, 776 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 89d190c7..ef347056 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,12 +259,6 @@ if(BUILD_NEW_VNCVIEWER) set(USE_INCLUDED_FLTK 1) endif() if(USE_INCLUDED_FLTK) - set(HAVE_FLTK_DEAD_KEYS 0) - set(HAVE_FLTK_CLIPBOARD 0) - set(HAVE_FLTK_MEDIAKEYS 0) - set(HAVE_FLTK_FULLSCREEN 0) - set(HAVE_FLTK_CURSOR 0) - set(FLTK_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/common/fltk) set(FLTK_LIBRARIES) if(APPLE) diff --git a/common/fltk/FL/Enumerations.H b/common/fltk/FL/Enumerations.H index 093422bf..219d623e 100644 --- a/common/fltk/FL/Enumerations.H +++ b/common/fltk/FL/Enumerations.H @@ -292,7 +292,12 @@ enum Fl_Event { // events If the widget returns 1, it will receive the data in the immediately following FL_PASTE event. */ - FL_DND_RELEASE = 23 + FL_DND_RELEASE = 23, + + /** The fullscreen state of the window has changed + */ + FL_FULLSCREEN = 24 + }; /** \name When Conditions */ @@ -874,35 +879,36 @@ inline Fl_Color fl_color_cube(int r, int g, int b) { /** The following constants define the mouse cursors that are available in FLTK. - The double-headed arrows are bitmaps provided by FLTK on X, the others - are provided by system-defined cursors. + Cursors are provided by the system when available, or bitmaps built into + FLTK as a fallback. \todo enum Fl_Cursor needs maybe an image. */ enum Fl_Cursor { - FL_CURSOR_DEFAULT = 0, /**< the default cursor, usually an arrow. */ - FL_CURSOR_ARROW = 35, /**< an arrow pointer. */ - FL_CURSOR_CROSS = 66, /**< crosshair. */ - FL_CURSOR_WAIT = 76, /**< watch or hourglass. */ - FL_CURSOR_INSERT = 77, /**< I-beam. */ - FL_CURSOR_HAND = 31, /**< hand (uparrow on MSWindows). */ - FL_CURSOR_HELP = 47, /**< question mark. */ - FL_CURSOR_MOVE = 27, /**< 4-pointed arrow. */ - // fltk provides bitmaps for these: - FL_CURSOR_NS = 78, /**< up/down arrow. */ - FL_CURSOR_WE = 79, /**< left/right arrow. */ - FL_CURSOR_NWSE = 80, /**< diagonal arrow. */ - FL_CURSOR_NESW = 81, /**< diagonal arrow. */ - FL_CURSOR_NONE =255, /**< invisible. */ - // for back compatibility (non MSWindows ones): - FL_CURSOR_N = 70, /**< for back compatibility. */ - FL_CURSOR_NE = 69, /**< for back compatibility. */ - FL_CURSOR_E = 49, /**< for back compatibility. */ - FL_CURSOR_SE = 8, /**< for back compatibility. */ - FL_CURSOR_S = 9, /**< for back compatibility. */ - FL_CURSOR_SW = 7, /**< for back compatibility. */ - FL_CURSOR_W = 36, /**< for back compatibility. */ - FL_CURSOR_NW = 68 /**< for back compatibility. */ + FL_CURSOR_DEFAULT = 0, /**< the default cursor, usually an arrow. */ + FL_CURSOR_ARROW = 1, /**< an arrow pointer. */ + FL_CURSOR_CROSS = 2, /**< crosshair. */ + FL_CURSOR_WAIT = 3, /**< busy indicator (e.g. hourglass). */ + FL_CURSOR_INSERT = 4, /**< I-beam. */ + FL_CURSOR_HAND = 5, /**< pointing hand. */ + FL_CURSOR_HELP = 6, /**< question mark pointer. */ + FL_CURSOR_MOVE = 7, /**< 4-pointed arrow or hand. */ + + /* Resize indicators */ + FL_CURSOR_NS = 101, /**< up/down resize. */ + FL_CURSOR_WE = 102, /**< left/right resize. */ + FL_CURSOR_NWSE = 103, /**< diagonal resize. */ + FL_CURSOR_NESW = 104, /**< diagonal resize. */ + FL_CURSOR_NE = 110, /**< upwards, right resize. */ + FL_CURSOR_N = 111, /**< upwards resize. */ + FL_CURSOR_NW = 112, /**< upwards, left resize. */ + FL_CURSOR_E = 113, /**< leftwards resize. */ + FL_CURSOR_W = 114, /**< rightwards resize. */ + FL_CURSOR_SE = 115, /**< downwards, right resize. */ + FL_CURSOR_S = 116, /**< downwards resize. */ + FL_CURSOR_SW = 117, /**< downwards, left resize. */ + + FL_CURSOR_NONE = 255, /**< invisible. */ }; /*@}*/ // group: Cursors diff --git a/common/fltk/FL/Fl.H b/common/fltk/FL/Fl.H index 9aaa1edd..e00735f6 100644 --- a/common/fltk/FL/Fl.H +++ b/common/fltk/FL/Fl.H @@ -108,6 +108,9 @@ typedef int (*Fl_Args_Handler)(int argc, char **argv, int &i); \see Fl::event_dispatch(Fl_Event_Dispatch) */ typedef int (*Fl_Event_Dispatch)(int event, Fl_Window *w); +/** Signature of add_clipboard_notify functions passed as parameters */ +typedef void (*Fl_Clipboard_Notify_Handler)(int source, void *data); + /** @} */ /* group callback_functions */ @@ -744,6 +747,19 @@ public: */ static void paste(Fl_Widget &receiver, int source /*=0*/); // platform dependent /** + FLTK will call the registered callback whenever there is a change to the + selection buffer or the clipboard. The source argument indicates which + of the two has changed. Only changes by other applications are reported. + \note Some systems require polling to monitor the clipboard and may + therefore have some delay in detecting changes. + */ + static void add_clipboard_notify(Fl_Clipboard_Notify_Handler h, void *data); + /** + Stop calling the specified callback when there are changes to the selection + buffer or the clipboard. + */ + static void remove_clipboard_notify(Fl_Clipboard_Notify_Handler h); + /** Initiate a Drag And Drop operation. The selection buffer should be filled with relevant data before calling this method. FLTK will then initiate the system wide drag and drop handling. Dropped data diff --git a/common/fltk/FL/Fl_Image.H b/common/fltk/FL/Fl_Image.H index eb1e7e3f..8be991a5 100644 --- a/common/fltk/FL/Fl_Image.H +++ b/common/fltk/FL/Fl_Image.H @@ -34,6 +34,7 @@ # include "Enumerations.H" class Fl_Widget; +class Fl_Pixmap; struct Fl_Menu_Item; struct Fl_Label; @@ -196,6 +197,7 @@ public: /** The constructor creates a new image from the specified data. */ Fl_RGB_Image(const uchar *bits, int W, int H, int D=3, int LD=0) : Fl_Image(W,H,D), array(bits), alloc_array(0), id_(0), mask_(0) {data((const char **)&array, 1); ld(LD);} + Fl_RGB_Image(const Fl_Pixmap *pxm, Fl_Color bg=FL_GRAY); virtual ~Fl_RGB_Image(); virtual Fl_Image *copy(int W, int H); Fl_Image *copy() { return copy(w(), h()); } diff --git a/common/fltk/FL/Fl_Widget.H b/common/fltk/FL/Fl_Widget.H index 780c6cba..041b2d36 100644 --- a/common/fltk/FL/Fl_Widget.H +++ b/common/fltk/FL/Fl_Widget.H @@ -108,6 +108,7 @@ struct FL_EXPORT Fl_Label { */ class FL_EXPORT Fl_Widget { friend class Fl_Group; + friend class Fl_X; Fl_Group* parent_; Fl_Callback* callback_; @@ -179,6 +180,8 @@ protected: NO_OVERLAY = 1<<15, ///< window not using a hardware overlay plane (Fl_Menu_Window) GROUP_RELATIVE = 1<<16, ///< position this widget relative to the parent group, not to the window COPIED_TOOLTIP = 1<<17, ///< the widget tooltip is internally copied, its destruction is handled by the widget + SIMPLE_KEYBOARD = 1<<18, ///< the widget wants simple, consistent keypresses and not advanced input (like character composition and CJK input) + FULLSCREEN = 1<<19, ///< a fullscreen window (Fl_Window) // (space for more flags) USERFLAG3 = 1<<29, ///< reserved for 3rd party extensions USERFLAG2 = 1<<30, ///< reserved for 3rd party extensions @@ -784,6 +787,35 @@ public: */ void clear_changed() {flags_ &= ~CHANGED;} + /** + Returns if the widget sees a simplified keyboard model or not. + + Normally widgets get a full-featured keyboard model that is geared + towards text input. This includes support for compose sequences and + advanced input methods, commonly used for asian writing system. This + system however has downsides in that extra graphic can be presented + to the user and that a physical key press doesn't correspond directly + to a FLTK event. + + Widgets that need a direct correspondence between actual key events + and those seen by the widget can swith to the simplified keyboard + model. + + \retval 0 if the widget uses the normal keyboard model + \see set_changed(), clear_changed() + */ + unsigned int simple_keyboard() const {return flags_&SIMPLE_KEYBOARD;} + + /** Marks a widget to use the simple keyboard model. + \see changed(), clear_changed() + */ + void set_simple_keyboard() {flags_ |= SIMPLE_KEYBOARD;} + + /** Marks a widget to use the normal keyboard model. + \see changed(), set_changed() + */ + void set_normal_keyboard() {flags_ &= ~SIMPLE_KEYBOARD;} + /** Gives the widget the keyboard focus. Tries to make this widget be the Fl::focus() widget, by first sending it an FL_FOCUS event, and if it returns non-zero, setting @@ -852,6 +884,9 @@ public: static unsigned int label_shortcut(const char *t); /* Internal use only. */ static int test_shortcut(const char*, const bool require_alt = false); + /* Internal use only. */ + void _set_fullscreen() {flags_ |= FULLSCREEN;} + void _clear_fullscreen() {flags_ &= ~FULLSCREEN;} /** Checks if w is a child of this widget. \param[in] w potential child widget diff --git a/common/fltk/FL/Fl_Window.H b/common/fltk/FL/Fl_Window.H index 8317fb4a..02ba6884 100644 --- a/common/fltk/FL/Fl_Window.H +++ b/common/fltk/FL/Fl_Window.H @@ -37,6 +37,7 @@ #define FL_DOUBLE_WINDOW 0xF1 ///< double window type id class Fl_X; +class Fl_RGB_Image; /** This widget produces an actual window. This can either be a main @@ -69,9 +70,9 @@ class FL_EXPORT Fl_Window : public Fl_Group { int minw, minh, maxw, maxh; int dw, dh, aspect; uchar size_range_set; + int no_fullscreen_x, no_fullscreen_y, no_fullscreen_w, no_fullscreen_h; // cursor stuff Fl_Cursor cursor_default; - Fl_Color cursor_fg, cursor_bg; void size_range_(); void _Fl_Window(); // constructor innards @@ -384,15 +385,27 @@ public: /** Makes the window completely fill the screen, without any window manager border visible. You must use fullscreen_off() to undo - this. This may not work with all window managers. + this. + + \note On some platforms, this can result in the keyboard being + grabbed. The window may also be recreated, meaning hide() and + show() will be called. */ void fullscreen(); /** + Turns off any side effects of fullscreen() + */ + void fullscreen_off(); + /** Turns off any side effects of fullscreen() and does resize(x,y,w,h). */ void fullscreen_off(int,int,int,int); /** + Returns non zero if FULLSCREEN flag is set, 0 otherwise. + */ + unsigned int fullscreen_active() const { return flags() & FULLSCREEN; } + /** Iconifies the window. If you call this when shown() is false it will show() it as an icon. If the window is already iconified this does nothing. @@ -434,14 +447,17 @@ public: is different. The type Fl_Cursor is an enumeration defined in <FL/Enumerations.H>. - (Under X you can get any XC_cursor value by passing - Fl_Cursor((XC_foo/2)+1)). The colors only work on X, they are - not implemented on WIN32. - For back compatibility only. + \see cursor(const Fl_RGB_Image*, int, int), default_cursor() */ - void cursor(Fl_Cursor, Fl_Color=FL_BLACK, Fl_Color=FL_WHITE); // platform dependent - void default_cursor(Fl_Cursor, Fl_Color=FL_BLACK, Fl_Color=FL_WHITE); + void cursor(Fl_Cursor); + void cursor(const Fl_RGB_Image*, int, int); + void default_cursor(Fl_Cursor); + + /* for legacy compatibility */ + void cursor(Fl_Cursor c, Fl_Color, Fl_Color=FL_WHITE) { cursor(c); }; + void default_cursor(Fl_Cursor c, Fl_Color, Fl_Color=FL_WHITE) { default_cursor(c); }; + static void default_callback(Fl_Window*, void* v); /** Returns the window width including any frame added by the window manager. diff --git a/common/fltk/FL/fl_draw.H b/common/fltk/FL/fl_draw.H index 93c0ce1a..eae2b7fa 100644 --- a/common/fltk/FL/fl_draw.H +++ b/common/fltk/FL/fl_draw.H @@ -757,7 +757,8 @@ FL_EXPORT const char* fl_shortcut_label(unsigned int shortcut, const char **eom) FL_EXPORT unsigned int fl_old_shortcut(const char* s); FL_EXPORT void fl_overlay_rect(int x,int y,int w,int h); FL_EXPORT void fl_overlay_clear(); -FL_EXPORT void fl_cursor(Fl_Cursor, Fl_Color fg=FL_BLACK, Fl_Color bg=FL_WHITE); +FL_EXPORT void fl_cursor(Fl_Cursor); +FL_EXPORT void fl_cursor(Fl_Cursor, Fl_Color fg, Fl_Color bg=FL_WHITE); FL_EXPORT const char* fl_expand_text(const char* from, char* buf, int maxbuf, double maxw, int& n, double &width, int wrap, int draw_symbols = 0); diff --git a/common/fltk/FL/mac.H b/common/fltk/FL/mac.H index 704f521d..a8f88dba 100644 --- a/common/fltk/FL/mac.H +++ b/common/fltk/FL/mac.H @@ -141,7 +141,8 @@ public: void collapse(void); WindowRef window_ref(void); void set_key_window(void); - void set_cursor(Fl_Cursor); + int set_cursor(Fl_Cursor); + int set_cursor(const Fl_RGB_Image*, int, int); static CGImageRef CGImage_from_window_rect(Fl_Window *win, int x, int y, int w, int h); static unsigned char *bitmap_from_window_rect(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel); static Fl_Region intersect_region_and_rect(Fl_Region current, int x,int y,int w, int h); diff --git a/common/fltk/FL/names.h b/common/fltk/FL/names.h index 952acc02..614547aa 100644 --- a/common/fltk/FL/names.h +++ b/common/fltk/FL/names.h @@ -75,6 +75,7 @@ const char * const fl_eventnames[] = "FL_DND_DRAG", "FL_DND_LEAVE", "FL_DND_RELEASE", + "FL_FULLSCREEN" }; /** diff --git a/common/fltk/FL/win32.H b/common/fltk/FL/win32.H index 2150505a..2c682666 100644 --- a/common/fltk/FL/win32.H +++ b/common/fltk/FL/win32.H @@ -92,6 +92,8 @@ public: void flush() {w->flush();} void set_minmax(LPMINMAXINFO minmax); void mapraise(); + int set_cursor(Fl_Cursor); + int set_cursor(const Fl_RGB_Image*, int, int); static Fl_X* make(Fl_Window*); }; extern FL_EXPORT HCURSOR fl_default_cursor; diff --git a/common/fltk/FL/x.H b/common/fltk/FL/x.H index 002e429f..a5202862 100644 --- a/common/fltk/FL/x.H +++ b/common/fltk/FL/x.H @@ -163,6 +163,8 @@ public: static Fl_X* i(const Fl_Window* wi) {return wi->i;} void setwindow(Fl_Window* wi) {w=wi; wi->i=this;} void sendxjunk(); + int set_cursor(Fl_Cursor); + int set_cursor(const Fl_RGB_Image*, int, int); static void make_xid(Fl_Window*,XVisualInfo* =fl_visual, Colormap=fl_colormap); static Fl_X* set_xid(Fl_Window*, Window); // kludges to get around protection: diff --git a/common/fltk/src/Fl.cxx b/common/fltk/src/Fl.cxx index 900ff8f6..b0cb020f 100644 --- a/common/fltk/src/Fl.cxx +++ b/common/fltk/src/Fl.cxx @@ -79,6 +79,8 @@ void fl_cleanup_dc_list(void); extern double fl_mac_flush_and_wait(double time_to_wait, char in_idle); #endif // WIN32 +extern void fl_update_focus(void); + // // Globals... // @@ -444,6 +446,69 @@ static char in_idle; #endif //////////////////////////////////////////////////////////////// +// Clipboard notifications + +struct Clipboard_Notify { + Fl_Clipboard_Notify_Handler handler; + void *data; + struct Clipboard_Notify *next; +}; + +static struct Clipboard_Notify *clip_notify_list = NULL; + +extern void fl_clipboard_notify_change(); // in Fl_<platform>.cxx + +void Fl::add_clipboard_notify(Fl_Clipboard_Notify_Handler h, void *data) { + struct Clipboard_Notify *node; + + remove_clipboard_notify(h); + + node = new Clipboard_Notify; + + node->handler = h; + node->data = data; + node->next = clip_notify_list; + + clip_notify_list = node; + + fl_clipboard_notify_change(); +} + +void Fl::remove_clipboard_notify(Fl_Clipboard_Notify_Handler h) { + struct Clipboard_Notify *node, **prev; + + node = clip_notify_list; + prev = &clip_notify_list; + while (node != NULL) { + if (node->handler == h) { + *prev = node->next; + delete node; + + fl_clipboard_notify_change(); + + return; + } + + prev = &node->next; + node = node->next; + } +} + +bool fl_clipboard_notify_empty(void) { + return clip_notify_list == NULL; +} + +void fl_trigger_clipboard_notify(int source) { + struct Clipboard_Notify *node; + + node = clip_notify_list; + while (node != NULL) { + node->handler(source, node->data); + node = node->next; + } +} + +//////////////////////////////////////////////////////////////// // wait/run/check/ready: void (*Fl::idle)(); // see Fl::add_idle.cxx for the add/remove functions @@ -880,6 +945,8 @@ void Fl::focus(Fl_Widget *o) { fl_oldfocus = p; } e_number = old_event; + // let the platform code do what it needs + fl_update_focus(); } } @@ -1361,7 +1428,10 @@ int Fl::handle_(int e, Fl_Window* window) //////////////////////////////////////////////////////////////// // hide() destroys the X window, it does not do unmap! -#if !defined(WIN32) && USE_XFT +#if defined(WIN32) +extern void fl_clipboard_notify_untarget(HWND wnd); +extern void fl_update_clipboard(void); +#elif USE_XFT extern void fl_destroy_xft_draw(Window); #endif @@ -1408,14 +1478,10 @@ void Fl_Window::hide() { #if defined(WIN32) // this little trick keeps the current clipboard alive, even if we are about // to destroy the window that owns the selection. - if (GetClipboardOwner()==ip->xid) { - Fl_Window *w1 = Fl::first_window(); - if (w1 && OpenClipboard(fl_xid(w1))) { - EmptyClipboard(); - SetClipboardData(CF_TEXT, NULL); - CloseClipboard(); - } - } + if (GetClipboardOwner()==ip->xid) + fl_update_clipboard(); + // Make sure we unlink this window from the clipboard chain + fl_clipboard_notify_untarget(ip->xid); // Send a message to myself so that I'll get out of the event loop... PostMessage(ip->xid, WM_APP, 0, 0); if (ip->private_dc) fl_release_dc(ip->xid, ip->private_dc); diff --git a/common/fltk/src/Fl_Image.cxx b/common/fltk/src/Fl_Image.cxx index 4117ba77..58157be4 100644 --- a/common/fltk/src/Fl_Image.cxx +++ b/common/fltk/src/Fl_Image.cxx @@ -172,6 +172,19 @@ Fl_Image::measure(const Fl_Label *lo, // I - Label // // RGB image class... // + +int fl_convert_pixmap(const char*const* cdata, uchar* out, Fl_Color bg); + +/** The constructor creates a new RGBA image from the specified Fl_Pixmap. */ +Fl_RGB_Image::Fl_RGB_Image(const Fl_Pixmap *pxm, Fl_Color bg): + Fl_Image(pxm->w(), pxm->h(), 4), id_(0), mask_(0) +{ + array = new uchar[w() * h() * d()]; + alloc_array = 1; + fl_convert_pixmap(pxm->data(), (uchar*)array, bg); + data((const char **)&array, 1); +} + /** The destructor free all memory and server resources that are used by the image. */ Fl_RGB_Image::~Fl_RGB_Image() { uncache(); diff --git a/common/fltk/src/Fl_Window.cxx b/common/fltk/src/Fl_Window.cxx index 1aadb013..1ece624c 100644 --- a/common/fltk/src/Fl_Window.cxx +++ b/common/fltk/src/Fl_Window.cxx @@ -59,14 +59,16 @@ void Fl_Window::_Fl_Window() { resizable(0); size_range_set = 0; minw = maxw = minh = maxh = 0; + no_fullscreen_x = 0; + no_fullscreen_y = 0; + no_fullscreen_w = w(); + no_fullscreen_h = h(); callback((Fl_Callback*)default_callback); } Fl_Window::Fl_Window(int X,int Y,int W, int H, const char *l) : Fl_Group(X, Y, W, H, l) { cursor_default = FL_CURSOR_DEFAULT; - cursor_fg = FL_BLACK; - cursor_bg = FL_WHITE; _Fl_Window(); set_flag(FORCE_POSITION); @@ -76,8 +78,6 @@ Fl_Window::Fl_Window(int W, int H, const char *l) // fix common user error of a missing end() with current(0): : Fl_Group((Fl_Group::current(0),0), 0, W, H, l) { cursor_default = FL_CURSOR_DEFAULT; - cursor_fg = FL_BLACK; - cursor_bg = FL_WHITE; _Fl_Window(); clear_visible(); diff --git a/common/fltk/src/Fl_Window_fullscreen.cxx b/common/fltk/src/Fl_Window_fullscreen.cxx index ea563439..9e62a9d7 100644 --- a/common/fltk/src/Fl_Window_fullscreen.cxx +++ b/common/fltk/src/Fl_Window_fullscreen.cxx @@ -60,39 +60,48 @@ void Fl_Window::border(int b) { #endif } +void fullscreen_x(Fl_Window *w); +void fullscreen_off_x(); +void fullscreen_off_x(Fl_Window *w, int X, int Y, int W, int H); + +/* Note: The previous implementation toggled border(). With this new + implementation this is not necessary. Additionally, if we do that, + the application may lose focus when switching out of fullscreen + mode with some window managers. Besides, the API does not say that + the FLTK border state should be toggled; it only says that the + borders should not be *visible*. +*/ void Fl_Window::fullscreen() { -#ifndef WIN32 - //this would clobber the fake wm, since it relies on the border flags to - //determine its thickness - border(0); -#endif -#if defined(__APPLE__) || defined(WIN32) || defined(USE_X11) - int sx, sy, sw, sh; - Fl::screen_xywh(sx, sy, sw, sh, x(), y(), w(), h()); - // if we are on the main screen, we will leave the system menu bar unobstructed - if (Fl::x()>=sx && Fl::y()>=sy && Fl::x()+Fl::w()<=sx+sw && Fl::y()+Fl::h()<=sy+sh) { - sx = Fl::x(); sy = Fl::y(); - sw = Fl::w(); sh = Fl::h(); + if (shown() && !(flags() & Fl_Widget::FULLSCREEN)) { + no_fullscreen_x = x(); + no_fullscreen_y = y(); + no_fullscreen_w = w(); + no_fullscreen_h = h(); + fullscreen_x(this); + } else { + set_flag(FULLSCREEN); } - if (x()==sx) x(sx+1); // make sure that we actually execute the resize -#if defined(USE_X11) - resize(0, 0, w(), h()); // work around some quirks in X11 -#endif - resize(sx, sy, sw, sh); -#else - if (!x()) x(1); // make sure that we actually execute the resize - resize(0,0,Fl::w(),Fl::h()); -#endif } void Fl_Window::fullscreen_off(int X,int Y,int W,int H) { - // this order produces less blinking on IRIX: - resize(X,Y,W,H); -#ifndef WIN32 - border(1); -#endif + if (shown() && (flags() & Fl_Widget::FULLSCREEN)) { + fullscreen_off_x(this, X, Y, W, H); + } else { + clear_flag(FULLSCREEN); + } + no_fullscreen_x = no_fullscreen_y = no_fullscreen_w = no_fullscreen_h = 0; } +void Fl_Window::fullscreen_off() { + if (!no_fullscreen_x && !no_fullscreen_y) { + // Window was initially created fullscreen - default to current monitor + no_fullscreen_x = x(); + no_fullscreen_y = y(); + } + fullscreen_off(no_fullscreen_x, no_fullscreen_y, no_fullscreen_w, no_fullscreen_h); +} + + // // End of "$Id: Fl_Window_fullscreen.cxx 8515 2011-03-12 21:36:21Z manolo $". // diff --git a/common/fltk/src/Fl_cocoa.mm b/common/fltk/src/Fl_cocoa.mm index c1135640..5fd52d68 100644 --- a/common/fltk/src/Fl_cocoa.mm +++ b/common/fltk/src/Fl_cocoa.mm @@ -61,6 +61,7 @@ extern "C" { #include <stdarg.h> #import <Cocoa/Cocoa.h> +#import <Carbon/Carbon.h> #ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h #if defined(__LP64__) && __LP64__ @@ -107,7 +108,6 @@ int fl_screen; CGContextRef fl_gc = 0; void *fl_system_menu; // this is really a NSMenu* Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0; -void *fl_default_cursor; // this is really a NSCursor* void *fl_capture = 0; // (NSWindow*) we need this to compensate for a missing(?) mouse capture bool fl_show_iconic; // true if called from iconize() - shows the next created window in collapsed state //int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR @@ -124,6 +124,8 @@ static Fl_Window* send_motion; extern Fl_Window* fl_xmousewin; #endif +bool use_simple_keyboard = false; + enum { FLTKTimerEvent = 1, FLTKDataReadyEvent }; @@ -140,6 +142,39 @@ void fl_set_status(int x, int y, int w, int h) { } +// Undocumented voodoo. Taken from Mozilla. +#define ENABLE_ROMAN_KYBDS_ONLY -23 + +void fl_update_focus(void) +{ + Fl_Widget *focus; + + focus = Fl::grab(); + if (!focus) + focus = Fl::focus(); + if (!focus) + return; + + if (focus->simple_keyboard()) + use_simple_keyboard = true; + else + use_simple_keyboard = false; + + // Force a "Roman" or "ASCII" keyboard, which both the Mozilla and + // Safari people seem to think implies turning off advanced IME stuff + // (see nsTSMManager::SyncKeyScript in Mozilla and enableSecureTextInput + // in Safari/Webcore). Should be good enough for us then... +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + CFArrayRef inputSources = TISCreateASCIICapableInputSourceList(); + TSMSetDocumentProperty(TSMGetActiveDocument(), + kTSMDocumentEnabledInputSourcesPropertyTag, + sizeof(CFArrayRef), &inputSources); + CFRelease(inputSources); +#else + KeyScript(use_simple_keyboard ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds); +#endif +} + /* * Mac keyboard lookup table * See also the inverse converter vktab in Fl_get_key_mac.cxx @@ -614,6 +649,10 @@ static void do_timer(CFRunLoopTimerRef timer, void* data) { containsGLsubwindow = contains; } +- (BOOL)canBecomeKeyWindow +{ + return YES; +} @end @interface FLApplication : NSObject @@ -843,6 +882,25 @@ static void cocoaMouseHandler(NSEvent *theEvent) } @end +static const char* cocoaDead2FLTK(const char *in) +{ + if (strcmp(in, "\140") == 0) // GRAVE ACCENT + return "\314\200"; // COMBINING GRAVE ACCENT + if (strcmp(in, "\302\264") == 0) // ACUTE ACCENT + return "\314\201"; // COMBINING ACUTE ACCENT + if (strcmp(in, "\136") == 0) // CIRCUMFLEX ACCENT + return "\314\202"; // COMBINING CIRCUMFLEX ACCENT + if (strcmp(in, "\176") == 0) // TILDE + return "\314\203"; // COMBINING TILDE + if (strcmp(in, "\302\250") == 0) // DIAERESIS + return "\314\210"; // COMBINING DIAERESIS + // FIXME: OS X dead key behaviour isn't documented and I don't have + // any more keyboards to test with... + + // hope that OS X gave us something proper to begin with + return in; +} + /* Handle cocoa keyboard events Events during a character composition sequence: @@ -1033,6 +1091,10 @@ extern "C" { fl_lock_function(); FLWindow *nsw = (FLWindow*)[notif object]; Fl_Window *window = [nsw getFl_Window]; + /* Fullscreen windows obscure all other windows so we need to return + to a "normal" level when the user switches to another window */ + if (window->fullscreen_active()) + [nsw setLevel:NSNormalWindowLevel]; Fl::handle( FL_UNFOCUS, window); fl_unlock_function(); } @@ -1041,6 +1103,9 @@ extern "C" { fl_lock_function(); FLWindow *nsw = (FLWindow*)[notif object]; Fl_Window *w = [nsw getFl_Window]; + /* Restore previous fullscreen level */ + if (w->fullscreen_active()) + [nsw setLevel:NSStatusWindowLevel]; if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w); fl_unlock_function(); } @@ -1228,9 +1293,13 @@ extern "C" { } @end +static void clipboard_check(void); + @implementation FLApplication + (void)sendEvent:(NSEvent *)theEvent { + // update clipboard status + clipboard_check(); NSEventType type = [theEvent type]; if (type == NSLeftMouseDown) { fl_lock_function(); @@ -1285,8 +1354,6 @@ void fl_open_display() { dequeue:YES]; while (ign_event); - fl_default_cursor = [NSCursor arrowCursor]; - // bring the application into foreground without a 'CARB' resource Boolean same_psn; ProcessSerialNumber cur_psn, front_psn; @@ -1586,6 +1653,7 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi - (void)drawRect:(NSRect)rect; - (BOOL)acceptsFirstResponder; - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent; +- (void)resetCursorRects; - (BOOL)performKeyEquivalent:(NSEvent*)theEvent; - (void)mouseUp:(NSEvent *)theEvent; - (void)rightMouseUp:(NSEvent *)theEvent; @@ -1643,6 +1711,16 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi Fl_Window *first = Fl::first_window(); return (first == w || !first->modal()); } +- (void)resetCursorRects { + Fl_Window *w = [(FLWindow*)[self window] getFl_Window]; + Fl_X *i = Fl_X::i(w); + // We have to have at least one cursor rect for invalidateCursorRectsForView + // to work, hence the "else" clause. + if (i->cursor) + [self addCursorRect:[self visibleRect] cursor:(NSCursor*)i->cursor]; + else + [self addCursorRect:[self visibleRect] cursor:[NSCursor arrowCursor]]; +} - (void)mouseUp:(NSEvent *)theEvent { cocoaMouseHandler(theEvent); } @@ -1702,8 +1780,13 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi break; } } + // Don't send cmd-<key> to interpretKeyEvents because it beeps. if (!no_text_key && !(Fl::e_state & FL_META) ) { - // Don't send cmd-<key> to interpretKeyEvents because it beeps. + // The simple keyboard model will ignore insertText, so we need to grab + // the symbol directly from the event. Note that we still use setMarkedText. + if (use_simple_keyboard) + [FLView prepareEtext:[theEvent charactersIgnoringModifiers]]; + // Then we can let the OS have a stab at it and see if it thinks it // should result in some text NSText *edit = [[theEvent window] fieldEditor:YES forObject:nil]; @@ -1882,21 +1965,30 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi //NSLog(@"insertText: received=%@",received); if (!in_key_event) fl_lock_function(); + + // Simple keyboard widgets do not want these side channel inputs. + if (use_simple_keyboard) + goto end; + [FLView prepareEtext:received]; + // We can get called outside of key events (e.g. from the character - // palette). Transform such actions to FL_PASTE events. + // palette). We need to fake our own key event at that point. if (!in_key_event) { Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - Fl::handle(FL_PASTE, target); + Fl::e_keysym = Fl::e_original_keysym = 0; + Fl::handle(FL_KEYDOWN, target); // for some reason, the window does not redraw until the next mouse move or button push // sending a 'redraw()' or 'awake()' does not solve the issue! Fl::flush(); } + +end: if (!in_key_event) fl_unlock_function(); } - (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection { - NSString *received; + NSString *received, *current, *aggregate; if (newSelection.location == 0) { [self unmarkText]; return; @@ -1907,11 +1999,47 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi received = (NSString*)aString; } //NSLog(@"setMarkedText: %@ %d %d",received,newSelection.location,newSelection.length); + + fl_lock_function(); + + // Simple keyboard widgets generally do not want these side channel + // inputs, but we have no other way of getting dead keys so we make + // an exception in that case. + if (use_simple_keyboard) { + if (in_key_event && (Fl::e_length == 0)) { + [FLView prepareEtext:received]; + + Fl::e_text = (char*)cocoaDead2FLTK(Fl::e_text); + Fl::e_length = strlen(Fl::e_text); + } + goto end; + } + // This code creates the OS X behaviour of seeing dead keys as things // are being composed. + // + // Note: The concatenation thing is because of how OS X deals with + // invalid sequences. At that point it will spit out one call + // to insertText with the now aborted sequence, and one new + // call to setMarkedText with the new sequence. Since we want + // both to be visible, we need to concatenate. next_compose_length = newSelection.location; - [FLView prepareEtext:received]; - //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", received, Fl::e_length, next_compose_length); + current = [NSString stringWithUTF8String:Fl::e_text]; + aggregate = [current stringByAppendingString:received]; + + [FLView prepareEtext:aggregate]; + //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", aggregate, Fl::e_length, next_compose_length); + + // We can get called outside of key events (e.g. from the character + // palette). We need to fake our own key event at that point. + if (!in_key_event) { + Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; + Fl::e_keysym = Fl::e_original_keysym = 0; + Fl::handle(FL_KEYDOWN, target); + } + +end: + fl_unlock_function(); } - (void)unmarkText { @@ -1981,6 +2109,22 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi @end +void fullscreen_x(Fl_Window *w) { + w->_set_fullscreen(); + /* On OS X < 10.6, it is necessary to recreate the window. This is done + with hide+show. */ + w->hide(); + w->show(); + Fl::handle(FL_FULLSCREEN, w); +} + +void fullscreen_off_x(Fl_Window *w, int X, int Y, int W, int H) { + w->_clear_fullscreen(); + w->hide(); + w->resize(X, Y, W, H); + w->show(); + Fl::handle(FL_FULLSCREEN, w); +} /* * go ahead, create that (sub)window @@ -1996,7 +2140,7 @@ void Fl_X::make(Fl_Window* w) x->other_xid = 0; x->region = 0; x->subRegion = 0; - x->cursor = fl_default_cursor; + x->cursor = NULL; x->gc = 0; // stay 0 for Quickdraw; fill with CGContext for Quartz Fl_Window *win = w->window(); Fl_X *xo = Fl_X::i(win); @@ -2098,12 +2242,19 @@ void Fl_X::make(Fl_Window* w) x->other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows x->region = 0; x->subRegion = 0; - x->cursor = fl_default_cursor; + x->cursor = NULL; x->xidChildren = 0; x->xidNext = 0; x->gc = 0; NSRect srect = [[NSScreen mainScreen] frame]; + if (w->flags() & Fl_Widget::FULLSCREEN) { + int sx, sy, sw, sh; + Fl::screen_xywh(sx, sy, sw, sh, w->x(), w->y(), w->w(), w->h()); + w->resize(sx, sy, sw, sh); + winstyle = NSBorderlessWindowMask; + winlevel = NSStatusWindowLevel; + } NSRect crect; crect.origin.x = w->x(); crect.origin.y = srect.size.height - (w->y() + w->h()); @@ -2552,6 +2703,27 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) { receiver.handle(FL_PASTE); } +extern void fl_trigger_clipboard_notify(int source); + +void fl_clipboard_notify_change() { + // No need to do anything here... +} + +static void clipboard_check(void) +{ + PasteboardSyncFlags flags; + + allocatePasteboard(); + flags = PasteboardSynchronize(myPasteboard); + + if (!(flags & kPasteboardModified)) + return; + if (flags & kPasteboardClientIsOwner) + return; + + fl_trigger_clipboard_notify(1); +} + void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data) { // check, if this timer slot exists already @@ -2676,6 +2848,10 @@ void Fl_X::destroy() { [[(NSWindow *)xid contentView] release]; [(NSWindow *)xid close]; } + if (cursor) { + [(NSCursor*)cursor release]; + cursor = NULL; + } } void Fl_X::map() { @@ -2791,68 +2967,106 @@ static NSImage *CGBitmapContextToNSImage(CGContextRef c) return [image autorelease]; } -static NSCursor *PrepareCursor(NSCursor *cursor, CGContextRef (*f)() ) +int Fl_X::set_cursor(Fl_Cursor c) { - if (cursor == nil) { - CGContextRef c = f(); - NSImage *image = CGBitmapContextToNSImage(c); - fl_delete_offscreen( (Fl_Offscreen)c ); - NSPoint pt = {[image size].width/2, [image size].height/2}; - cursor = [[NSCursor alloc] initWithImage:image hotSpot:pt]; + if (cursor) { + [(NSCursor*)cursor release]; + cursor = NULL; } - return cursor; -} -void Fl_X::set_cursor(Fl_Cursor c) -{ - NSCursor *icrsr; switch (c) { - case FL_CURSOR_CROSS: icrsr = [NSCursor crosshairCursor]; break; - case FL_CURSOR_WAIT: - static NSCursor *watch = nil; - watch = PrepareCursor(watch, &Fl_X::watch_cursor_image); - icrsr = watch; - break; - case FL_CURSOR_INSERT: icrsr = [NSCursor IBeamCursor]; break; - case FL_CURSOR_N: icrsr = [NSCursor resizeUpCursor]; break; - case FL_CURSOR_S: icrsr = [NSCursor resizeDownCursor]; break; - case FL_CURSOR_NS: icrsr = [NSCursor resizeUpDownCursor]; break; - case FL_CURSOR_HELP: - static NSCursor *help = nil; - help = PrepareCursor(help, &Fl_X::help_cursor_image); - icrsr = help; - break; - case FL_CURSOR_HAND: icrsr = [NSCursor pointingHandCursor]; break; - case FL_CURSOR_MOVE: icrsr = [NSCursor openHandCursor]; break; - case FL_CURSOR_NE: - case FL_CURSOR_SW: - case FL_CURSOR_NESW: - static NSCursor *nesw = nil; - nesw = PrepareCursor(nesw, &Fl_X::nesw_cursor_image); - icrsr = nesw; - break; - case FL_CURSOR_E: icrsr = [NSCursor resizeRightCursor]; break; - case FL_CURSOR_W: icrsr = [NSCursor resizeLeftCursor]; break; - case FL_CURSOR_WE: icrsr = [NSCursor resizeLeftRightCursor]; break; - case FL_CURSOR_SE: - case FL_CURSOR_NW: - case FL_CURSOR_NWSE: - static NSCursor *nwse = nil; - nwse = PrepareCursor(nwse, &Fl_X::nwse_cursor_image); - icrsr = nwse; - break; - case FL_CURSOR_NONE: - static NSCursor *none = nil; - none = PrepareCursor(none, &Fl_X::none_cursor_image); - icrsr = none; - break; - case FL_CURSOR_ARROW: - case FL_CURSOR_DEFAULT: - default: icrsr = [NSCursor arrowCursor]; - break; + case FL_CURSOR_ARROW: cursor = [NSCursor arrowCursor]; break; + case FL_CURSOR_CROSS: cursor = [NSCursor crosshairCursor]; break; + case FL_CURSOR_INSERT: cursor = [NSCursor IBeamCursor]; break; + case FL_CURSOR_HAND: cursor = [NSCursor pointingHandCursor]; break; + case FL_CURSOR_MOVE: cursor = [NSCursor openHandCursor]; break; + case FL_CURSOR_NS: cursor = [NSCursor resizeUpDownCursor]; break; + case FL_CURSOR_WE: cursor = [NSCursor resizeLeftRightCursor]; break; + case FL_CURSOR_N: cursor = [NSCursor resizeUpCursor]; break; + case FL_CURSOR_E: cursor = [NSCursor resizeRightCursor]; break; + case FL_CURSOR_W: cursor = [NSCursor resizeLeftCursor]; break; + case FL_CURSOR_S: cursor = [NSCursor resizeDownCursor]; break; + default: + return 0; + } + + [(NSCursor*)cursor retain]; + + [(NSWindow*)xid invalidateCursorRectsForView:[(NSWindow*)xid contentView]]; + + return 1; +} + +int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { + if (cursor) { + [(NSCursor*)cursor release]; + cursor = NULL; + } + + if ((hotx < 0) || (hotx >= image->w())) + return 0; + if ((hoty < 0) || (hoty >= image->h())) + return 0; + + // OS X >= 10.6 can create a NSImage from a CGImage, but we need to + // support older versions, hence this pesky handling. + + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:image->w() + pixelsHigh:image->h() + bitsPerSample:8 + samplesPerPixel:image->d() + hasAlpha:!(image->d() & 1) + isPlanar:NO + colorSpaceName:(image->d()<=2) ? NSDeviceWhiteColorSpace : NSDeviceRGBColorSpace + bytesPerRow:(image->w() * image->d()) + bitsPerPixel:(image->d()*8)]; + + // Alpha needs to be premultiplied for this format + + const uchar *i = (const uchar*)*image->data(); + unsigned char *o = [bitmap bitmapData]; + for (int y = 0;y < image->h();y++) { + if (image->d() & 1) { + for (int x = 0;x < image->w();x++) { + unsigned int alpha; + if (image->d() == 4) { + alpha = i[3]; + *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); + *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); + } + + alpha = i[1]; + *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); + *o++ = alpha; + i++; + } + } else { + // No alpha, so we can just copy everything directly. + int len = image->w() * image->d(); + memcpy(o, i, len); + o += len; + i += len; + } + i += image->ld(); } - [icrsr set]; - cursor = icrsr; + + NSImage *nsimage = [[NSImage alloc] + initWithSize:NSMakeSize(image->w(), image->h())]; + + [nsimage addRepresentation:bitmap]; + + cursor = [[NSCursor alloc] + initWithImage:nsimage + hotSpot:NSMakePoint(hotx, hoty)]; + + [(NSWindow*)xid invalidateCursorRectsForView:[(NSWindow*)xid contentView]]; + + [bitmap release]; + [nsimage release]; + + return 1; } @interface FLaboutItemTarget : NSObject diff --git a/common/fltk/src/Fl_grab.cxx b/common/fltk/src/Fl_grab.cxx index 76c9e9e1..38724bdd 100644 --- a/common/fltk/src/Fl_grab.cxx +++ b/common/fltk/src/Fl_grab.cxx @@ -38,6 +38,7 @@ // override_redirect, it does similar things on WIN32. extern void fl_fix_focus(); // in Fl.cxx +void fl_update_focus(void); #ifdef WIN32 // We have to keep track of whether we have captured the mouse, since @@ -50,7 +51,19 @@ extern HWND fl_capture; extern void *fl_capture; #endif +#if !(defined(WIN32) || defined(__APPLE__)) +extern int ewmh_supported(); // from Fl_x.cxx +#endif + void Fl::grab(Fl_Window* win) { + Fl_Window *fullscreen_win = NULL; + for (Fl_Window *W = Fl::first_window(); W; W = Fl::next_window(W)) { + if (W->fullscreen_active()) { + fullscreen_win = W; + break; + } + } + if (win) { if (!grab_) { #ifdef WIN32 @@ -60,8 +73,9 @@ void Fl::grab(Fl_Window* win) { fl_capture = Fl_X::i(first_window())->xid; Fl_X::i(first_window())->set_key_window(); #else + Window xid = fullscreen_win ? fl_xid(fullscreen_win) : fl_xid(first_window()); XGrabPointer(fl_display, - fl_xid(first_window()), + xid, 1, ButtonPressMask|ButtonReleaseMask| ButtonMotionMask|PointerMotionMask, @@ -71,7 +85,7 @@ void Fl::grab(Fl_Window* win) { 0, fl_event_time); XGrabKeyboard(fl_display, - fl_xid(first_window()), + xid, 1, GrabModeAsync, GrabModeAsync, @@ -79,6 +93,7 @@ void Fl::grab(Fl_Window* win) { #endif } grab_ = win; + fl_update_focus(); } else { if (grab_) { #ifdef WIN32 @@ -87,13 +102,17 @@ void Fl::grab(Fl_Window* win) { #elif defined(__APPLE__) fl_capture = 0; #else + // We must keep the grab in the non-EWMH fullscreen case + if (!fullscreen_win || ewmh_supported()) { XUngrabKeyboard(fl_display, fl_event_time); + } XUngrabPointer(fl_display, fl_event_time); // this flush is done in case the picked menu item goes into // an infinite loop, so we don't leave the X server locked up: XFlush(fl_display); #endif grab_ = 0; + fl_update_focus(); fl_fix_focus(); } } diff --git a/common/fltk/src/Fl_win32.cxx b/common/fltk/src/Fl_win32.cxx index 54d9c813..d4ada8b1 100644 --- a/common/fltk/src/Fl_win32.cxx +++ b/common/fltk/src/Fl_win32.cxx @@ -98,6 +98,8 @@ FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_gdi_ Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_gdi_display; // the current target surface of graphics operations Fl_Display_Device *Fl_Display_Device::_display = &fl_gdi_display; // the platform display +bool use_simple_keyboard = false; + // dynamic wsock dll handling api: #if defined(__CYGWIN__) && !defined(SOCKET) # define SOCKET int @@ -131,6 +133,8 @@ static HMODULE get_wsock_mod() { * size and link dependencies. */ static HMODULE s_imm_module = 0; +typedef BOOL (WINAPI* flTypeImmAssociateContextEx)(HWND, HIMC, DWORD); +static flTypeImmAssociateContextEx flImmAssociateContextEx = 0; typedef HIMC (WINAPI* flTypeImmGetContext)(HWND); static flTypeImmGetContext flImmGetContext = 0; typedef BOOL (WINAPI* flTypeImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM); @@ -146,6 +150,7 @@ static HMODULE get_imm_module() { if (!s_imm_module) Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n" "Please check your input method manager library accessibility."); + flImmAssociateContextEx = (flTypeImmAssociateContextEx)GetProcAddress(s_imm_module, "ImmAssociateContextEx"); flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext"); flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow"); flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext"); @@ -424,7 +429,12 @@ int fl_wait(double time_to_wait) { } } - TranslateMessage(&fl_msg); + // Don't bother with key to character translation as we do + // it manually for simpley keyboard widgets. In fact, calling + // TranslateMessage() just makes it more difficult as it sets + // a bunch of internal state. + if (!use_simple_keyboard) + TranslateMessage(&fl_msg); DispatchMessageW(&fl_msg); have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE); } @@ -542,6 +552,36 @@ public: const char* GetValue() const { return(out); } }; +void fl_update_clipboard(void) { + HWND hwnd = fl_xid(Fl::first_window()); + + if (!hwnd) + return; + + if (!OpenClipboard(hwnd)) + return; + + EmptyClipboard(); + + int utf16_len = fl_utf8toUtf16(fl_selection_buffer[1], + fl_selection_length[1], 0, 0); + + HGLOBAL hMem = GlobalAlloc(GHND, utf16_len * 2 + 2); // moveable and zero'ed mem alloc. + LPVOID memLock = GlobalLock(hMem); + + fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], + (unsigned short*) memLock, utf16_len + 1); + + GlobalUnlock(hMem); + SetClipboardData(CF_UNICODETEXT, hMem); + + CloseClipboard(); + + // In case Windows managed to lob of a WM_DESTROYCLIPBOARD during + // the above. + fl_i_own_selection[1] = 1; +} + // call this when you create a selection: void Fl::copy(const char *stuff, int len, int clipboard) { if (!stuff || len<0) return; @@ -559,25 +599,9 @@ void Fl::copy(const char *stuff, int len, int clipboard) { memcpy(fl_selection_buffer[clipboard], stuff, len); fl_selection_buffer[clipboard][len] = 0; // needed for direct paste fl_selection_length[clipboard] = len; - if (clipboard) { - // set up for "delayed rendering": - if (OpenClipboard(NULL)) { - // if the system clipboard works, use it - int utf16_len = fl_utf8toUtf16(fl_selection_buffer[clipboard], fl_selection_length[clipboard], 0, 0); - EmptyClipboard(); - HGLOBAL hMem = GlobalAlloc(GHND, utf16_len * 2 + 2); // moveable and zero'ed mem alloc. - LPVOID memLock = GlobalLock(hMem); - fl_utf8toUtf16(fl_selection_buffer[clipboard], fl_selection_length[clipboard], (unsigned short*) memLock, utf16_len + 1); - GlobalUnlock(hMem); - SetClipboardData(CF_UNICODETEXT, hMem); - CloseClipboard(); - GlobalFree(hMem); - fl_i_own_selection[clipboard] = 0; - } else { - // only if it fails, instruct paste() to use the internal buffers - fl_i_own_selection[clipboard] = 1; - } - } + fl_i_own_selection[clipboard] = 1; + if (clipboard) + fl_update_clipboard(); } // Call this when a "paste" operation happens: @@ -630,6 +654,38 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) { } } +static HWND clipboard_wnd = 0; +static HWND next_clipboard_wnd = 0; + +static bool initial_clipboard = true; + +void fl_clipboard_notify_change() { + // No need to do anything here... +} + +void fl_clipboard_notify_target(HWND wnd) { + if (clipboard_wnd) + return; + + // We get one fake WM_DRAWCLIPBOARD immediately, which we therefore + // need to ignore. + initial_clipboard = true; + + clipboard_wnd = wnd; + next_clipboard_wnd = SetClipboardViewer(wnd); +} + +void fl_clipboard_notify_untarget(HWND wnd) { + if (wnd != clipboard_wnd) + return; + + ChangeClipboardChain(wnd, next_clipboard_wnd); + clipboard_wnd = next_clipboard_wnd = 0; + + if (Fl::first_window()) + fl_clipboard_notify_target(fl_xid(Fl::first_window())); +} + //////////////////////////////////////////////////////////////// char fl_is_ime = 0; void fl_get_codepage() @@ -649,6 +705,49 @@ void fl_get_codepage() } } +void fl_update_focus(void) +{ + Fl_Widget *focus; + Fl_Window *win; + + get_imm_module(); + + focus = Fl::grab(); + if (!focus) + focus = Fl::focus(); + if (!focus) + return; + + // Grabs are special in that events are sent to the first + // available window + if (focus == Fl::grab()) + win = Fl::first_window(); + else { + win = focus->as_window(); + if (!win) + win = focus->window(); + } + + if (!win) { + Fl::warning("Cannot find window for widget receiving focus"); + return; + } + + // No Win32 window created yet + if (!Fl_X::i(win) || !fl_xid(win)) + return; + + if (focus->simple_keyboard()) { + use_simple_keyboard = true; + if (flImmGetContext(fl_xid(win)) != 0) + flImmAssociateContextEx(fl_xid(win), 0, 0); + } else { + use_simple_keyboard = false; + if (flImmGetContext(fl_xid(win)) == 0) + flImmAssociateContextEx(fl_xid(win), 0, IACE_DEFAULT); + } +} + HWND fl_capture; static int mouse_event(Fl_Window *window, int what, int button, @@ -796,6 +895,27 @@ static int ms2fltk(int vk, int extended) { return extended ? extendedlut[vk] : vklut[vk]; } +static xchar msdead2fltk(xchar in) +{ + switch (in) { + case 0x0060: // GRAVE ACCENT + return 0x0300; // COMBINING GRAVE ACCENT + case 0x00b4: // ACUTE ACCENT + return 0x0301; // COMBINING ACUTE ACCENT + case 0x005e: // CIRCUMFLEX ACCENT + return 0x0302; // COMBINING CIRCUMFLEX ACCENT + case 0x007e: // TILDE + return 0x0303; // COMBINING TILDE + case 0x00a8: // DIAERESIS + return 0x0308; // COMBINING DIAERESIS + // FIXME: Windows dead key behaviour isn't documented and I don't have + // any more keyboards to test with... + } + + // hope that Windows gave us something proper to begin with + return in; +} + #if USE_COLORMAP extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx #endif @@ -857,6 +977,8 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar //fl_msg.pt = ??? //fl_msg.lPrivate = ??? + MSG fl_orig_msg = fl_msg; + Fl_Window *window = fl_find(hWnd); if (window) switch (uMsg) { @@ -1036,23 +1158,82 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK; Fl::e_state = state; static char buffer[1024]; - if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) { + if (use_simple_keyboard) { + BYTE keystate[256]; + WCHAR wbuf[8]; + int ret; + + // I'm not sure if we ever get WM_CHAR (& friends) without an initial + // WM_KEYDOWN (& friends), but if we do then we should not send such + // side band events to simple keyboard widgets. + if ((fl_orig_msg.message != WM_KEYDOWN) && + (fl_orig_msg.message != WM_SYSKEYDOWN) && + (fl_orig_msg.message != WM_KEYUP) && + (fl_orig_msg.message != WM_SYSKEYUP)) + break; + + GetKeyboardState(keystate); + + // Pressing Ctrl wreaks havoc with the symbol lookup, so turn that off. + // But AltGr shows up as Ctrl+Alt in Windows, so keep Ctrl if Alt is + // active. + if (!(keystate[VK_MENU] & (1<<31))) + keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; + + // We cannot inspect or modify Windows' internal state of the keyboard + // so we have to try to infer information from ToUnicode() and wedge + // things into a known state. + for (int i = 0;i < 2;i++) { + ret = ToUnicode(fl_orig_msg.wParam, 0, keystate, wbuf, + sizeof(wbuf)/sizeof(wbuf[0]), 0); + + // No symbol for this key (or unexpected length) + if ((ret == 0) || (ret < -1)) { + buffer[0] = 0; + Fl::e_length = 0; + break; + } + + // A dead key. Convert this to a Unicode combining character so + // that the application can tell the difference between dead and + // normal keys. + if (ret == -1) { + xchar u = (xchar) msdead2fltk(wbuf[0]); + Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); + buffer[Fl::e_length] = 0; + break; + } + + // If we have two characters (or more) from ToUnicode(), that's + // an invalid sequence. One character chould mean a proper symbol, + // or it could mean a composed one. In both cases we need to call + // ToUnicode() again to get something sane. + if (i == 0) + continue; + + // We should now have something sane. Give whatever we have to the + // application. + Fl::e_length = fl_utf8fromwc(buffer, 1024, wbuf, ret); + buffer[Fl::e_length] = 0; + } + } else if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) { xchar u = (xchar) wParam; // Fl::e_length = fl_unicode2utf(&u, 1, buffer); Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); buffer[Fl::e_length] = 0; + } else { + buffer[0] = 0; + Fl::e_length = 0; + } - - } else if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) { - if (state & FL_NUM_LOCK) { - // Convert to regular keypress... - buffer[0] = Fl::e_keysym-FL_KP; - Fl::e_length = 1; - } else { - // Convert to special keypress... - buffer[0] = 0; - Fl::e_length = 0; + // The keypad area is a bit odd in that we need to change the keysym + // to properly indicate what the user meant, unlike other keys where + // we normally change the text and keep keysym stable. + if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) { + // The initial mapping tables give us a keysym that corresponds to + // numlock being on, so we only do something when it is off. + if (!(state & FL_NUM_LOCK)) { switch (Fl::e_keysym) { case FL_KP + '0' : Fl::e_keysym = FL_Insert; @@ -1084,30 +1265,10 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar case FL_KP + '.' : Fl::e_keysym = FL_Delete; break; - case FL_KP + '/' : - case FL_KP + '*' : - case FL_KP + '-' : - case FL_KP + '+' : - buffer[0] = Fl::e_keysym-FL_KP; - Fl::e_length = 1; - break; } } - } else if ((lParam & (1<<31))==0) { -#ifdef FLTK_PREVIEW_DEAD_KEYS - if ((lParam & (1<<24))==0) { // clear if dead key (always?) - xchar u = (xchar) wParam; - Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); - buffer[Fl::e_length] = 0; - } else { // set if "extended key" (never printable?) - buffer[0] = 0; - Fl::e_length = 0; - } -#else - buffer[0] = 0; - Fl::e_length = 0; -#endif } + Fl::e_text = buffer; if (lParam & (1<<31)) { // key up events. if (Fl::handle(FL_KEYUP, window)) return 0; @@ -1125,12 +1286,28 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar case WM_MOUSEWHEEL: { static int delta = 0; // running total of all motion delta += (SHORT)(HIWORD(wParam)); + Fl::e_dx = 0; Fl::e_dy = -delta / WHEEL_DELTA; delta += Fl::e_dy * WHEEL_DELTA; if (Fl::e_dy) Fl::handle(FL_MOUSEWHEEL, window); return 0; } +// This is only defined on Vista and upwards... +#ifndef WM_MOUSEHWHEEL +#define WM_MOUSEHWHEEL 0x020E +#endif + + case WM_MOUSEHWHEEL: { + static int delta = 0; // running total of all motion + delta += (SHORT)(HIWORD(wParam)); + Fl::e_dy = 0; + Fl::e_dx = delta / WHEEL_DELTA; + delta -= Fl::e_dx * WHEEL_DELTA; + if (Fl::e_dx) Fl::handle(FL_MOUSEWHEEL, window); + return 0; + } + case WM_GETMINMAXINFO: Fl_X::i(window)->set_minmax((LPMINMAXINFO)lParam); break; @@ -1185,33 +1362,26 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar fl_i_own_selection[1] = 0; return 1; - case WM_RENDERALLFORMATS: - fl_i_own_selection[1] = 0; - // Windoze seems unhappy unless I do these two steps. Documentation - // seems to vary on whether opening the clipboard is necessary or - // is in fact wrong: - CloseClipboard(); - OpenClipboard(NULL); - // fall through... - case WM_RENDERFORMAT: { - HANDLE h; - -// int l = fl_utf_nb_char((unsigned char*)fl_selection_buffer[1], fl_selection_length[1]); - int l = fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], NULL, 0); // Pass NULL buffer to query length required - h = GlobalAlloc(GHND, (l+1) * sizeof(unsigned short)); - if (h) { - unsigned short *g = (unsigned short*) GlobalLock(h); -// fl_utf2unicode((unsigned char *)fl_selection_buffer[1], fl_selection_length[1], (xchar*)g); - l = fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], g, (l+1)); - g[l] = 0; - GlobalUnlock(h); - SetClipboardData(CF_UNICODETEXT, h); + case WM_CHANGECBCHAIN: + if ((hWnd == clipboard_wnd) && + (next_clipboard_wnd == (HWND)wParam)) { + next_clipboard_wnd = (HWND)lParam; + return 0; } + break; + + case WM_DRAWCLIPBOARD: + // When the clipboard moves between two FLTK windows, + // fl_i_own_selection will temporarily be false as we are + // processing this message. Hence the need to use fl_find(). + if (!initial_clipboard && !fl_find(GetClipboardOwner())) + fl_trigger_clipboard_notify(1); + initial_clipboard = false; - // Windoze also seems unhappy if I don't do this. Documentation very - // unclear on what is correct: - if (fl_msg.message == WM_RENDERALLFORMATS) CloseClipboard(); - return 1;} + if (next_clipboard_wnd) + SendMessage(next_clipboard_wnd, WM_DRAWCLIPBOARD, wParam, lParam); + + return 0; default: if (Fl::handle(0,0)) return 0; @@ -1320,6 +1490,11 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) X+=xoff; Y+=yoff; + if (w->flags() & Fl_Widget::FULLSCREEN) { + X = Y = 0; + bx = by = bt = 0; + } + return ret; } @@ -1370,6 +1545,58 @@ void Fl_Window::resize(int X,int Y,int W,int H) { } } +static void make_fullscreen(Fl_Window *w, Window xid, int X, int Y, int W, int H) { + int sx, sy, sw, sh; + Fl::screen_xywh(sx, sy, sw, sh, X, Y, W, H); + DWORD flags = GetWindowLong(xid, GWL_STYLE); + flags = flags & ~(WS_THICKFRAME|WS_CAPTION); + SetWindowLong(xid, GWL_STYLE, flags); + // SWP_NOSENDCHANGING is so that we can override size limits + SetWindowPos(xid, HWND_TOP, sx, sy, sw, sh, SWP_NOSENDCHANGING | SWP_FRAMECHANGED); +} + +void fullscreen_x(Fl_Window *w) { + w->_set_fullscreen(); + make_fullscreen(w, fl_xid(w), w->x(), w->y(), w->w(), w->h()); + Fl::handle(FL_FULLSCREEN, w); +} + +void fullscreen_off_x(Fl_Window *w, int X, int Y, int W, int H) { + w->_clear_fullscreen(); + DWORD style = GetWindowLong(fl_xid(w), GWL_STYLE); + // Remove the xid temporarily so that Fl_X::fake_X_wm() behaves like it + // does in Fl_X::make(). + HWND xid = fl_xid(w); + Fl_X::i(w)->xid = NULL; + int x, y, bt, bx, by; + switch (Fl_X::fake_X_wm(w, x, y, bt, bx, by)) { + case 0: + break; + case 1: + style |= WS_CAPTION; + break; + case 2: + if (w->border()) { + style |= WS_THICKFRAME | WS_CAPTION; + } + break; + } + Fl_X::i(w)->xid = xid; + // Adjust for decorations (but not if that puts the decorations + // outside the screen) + if ((X != w->x()) || (Y != w->y())) { + X -= bx; + Y -= by+bt; + } + W += bx*2; + H += by*2+bt; + SetWindowLong(fl_xid(w), GWL_STYLE, style); + SetWindowPos(fl_xid(w), 0, X, Y, W, H, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); + Fl::handle(FL_FULLSCREEN, w); +} + + //////////////////////////////////////////////////////////////// /* @@ -1408,7 +1635,6 @@ void fl_fix_focus(); // in Fl.cxx char fl_show_iconic; // hack for Fl_Window::iconic() // int fl_background_pixel = -1; // color to use for background -HCURSOR fl_default_cursor; UINT fl_wake_msg = 0; int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR @@ -1456,7 +1682,7 @@ Fl_X* Fl_X::make(Fl_Window* w) { if (!w->icon()) w->icon((void *)LoadIcon(NULL, IDI_APPLICATION)); wcw.hIcon = wcw.hIconSm = (HICON)w->icon(); - wcw.hCursor = fl_default_cursor = LoadCursor(NULL, IDC_ARROW); + wcw.hCursor = LoadCursor(NULL, IDC_ARROW); //uchar r,g,b; Fl::get_color(FL_GRAY,r,g,b); //wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(r,g,b)); wcw.hbrBackground = NULL; @@ -1499,18 +1725,26 @@ Fl_X* Fl_X::make(Fl_Window* w) { int xwm = xp , ywm = yp , bt, bx, by; switch (fake_X_wm(w, xwm, ywm, bt, bx, by)) { // No border (used for menus) - case 0: style |= WS_POPUP; - styleEx |= WS_EX_TOOLWINDOW; + case 0: + style |= WS_POPUP; + styleEx |= WS_EX_TOOLWINDOW; break; // Thin border and title bar - case 1: style |= WS_DLGFRAME | WS_CAPTION; break; + case 1: + style |= WS_DLGFRAME | WS_CAPTION; + if (!w->modal()) + style |= WS_SYSMENU | WS_MINIMIZEBOX; + break; // Thick, resizable border and title bar, with maximize button - case 2: style |= WS_THICKFRAME | WS_MAXIMIZEBOX | WS_CAPTION ; break; + case 2: + style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION; + if (!w->modal()) + style |= WS_MINIMIZEBOX; + break; } if (by+bt) { - if (!w->modal()) style |= WS_SYSMENU | WS_MINIMIZEBOX; wp += 2*bx; hp += 2*by+bt; } @@ -1540,7 +1774,7 @@ Fl_X* Fl_X::make(Fl_Window* w) { x->setwindow(w); x->region = 0; x->private_dc = 0; - x->cursor = fl_default_cursor; + x->cursor = LoadCursor(NULL, IDC_ARROW); if (!fl_codepage) fl_get_codepage(); WCHAR *lab = NULL; @@ -1566,9 +1800,23 @@ Fl_X* Fl_X::make(Fl_Window* w) { ); if (lab) free(lab); + if (w->flags() & Fl_Widget::FULLSCREEN) { + /* We need to make sure that the fullscreen is created on the + default monitor, ie the desktop where the shortcut is located + etc. This requires that CreateWindow is called with CW_USEDEFAULT + for x and y. We can then use GetWindowRect to determine which + monitor the window was placed on. */ + RECT rect; + GetWindowRect(x->xid, &rect); + make_fullscreen(w, x->xid, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top); + } + x->next = Fl_X::first; Fl_X::first = x; + fl_clipboard_notify_target(x->xid); + x->wait_for_expose = 1; if (fl_show_iconic) {showit = 0; fl_show_iconic = 0;} if (showit) { @@ -1581,7 +1829,7 @@ Fl_X* Fl_X::make(Fl_Window* w) { // If we've captured the mouse, we dont want to activate any // other windows from the code, or we lose the capture. ShowWindow(x->xid, !showit ? SW_SHOWMINNOACTIVE : - (Fl::grab() || (style & WS_POPUP)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL); + (Fl::grab() || (styleEx & WS_EX_TOOLWINDOW)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL); // Register all windows for potential drag'n'drop operations fl_OleInitialize(); @@ -1777,6 +2025,129 @@ void Fl_Window::label(const char *name,const char *iname) { } //////////////////////////////////////////////////////////////// + +#ifndef IDC_HAND +# define IDC_HAND MAKEINTRESOURCE(32649) +#endif // !IDC_HAND + +int Fl_X::set_cursor(Fl_Cursor c) { + LPSTR n; + + if (c == FL_CURSOR_NONE) + cursor = NULL; + else { + switch (c) { + case FL_CURSOR_ARROW: n = IDC_ARROW; break; + case FL_CURSOR_CROSS: n = IDC_CROSS; break; + case FL_CURSOR_WAIT: n = IDC_WAIT; break; + case FL_CURSOR_INSERT: n = IDC_IBEAM; break; + case FL_CURSOR_HAND: n = IDC_HAND; break; + case FL_CURSOR_HELP: n = IDC_HELP; break; + case FL_CURSOR_MOVE: n = IDC_SIZEALL; break; + case FL_CURSOR_N: + case FL_CURSOR_S: + // FIXME: Should probably have fallbacks for these instead + case FL_CURSOR_NS: n = IDC_SIZENS; break; + case FL_CURSOR_NE: + case FL_CURSOR_SW: + // FIXME: Dito. + case FL_CURSOR_NESW: n = IDC_SIZENESW; break; + case FL_CURSOR_E: + case FL_CURSOR_W: + // FIXME: Dito. + case FL_CURSOR_WE: n = IDC_SIZEWE; break; + case FL_CURSOR_SE: + case FL_CURSOR_NW: + // FIXME: Dito. + case FL_CURSOR_NWSE: n = IDC_SIZENWSE; break; + default: + return 0; + } + + cursor = LoadCursor(NULL, n); + if (cursor == NULL) + return 0; + } + + SetCursor(cursor); + + return 1; +} + +int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { + BITMAPV5HEADER bi; + HBITMAP bitmap, mask; + DWORD *bits; + + if ((hotx < 0) || (hotx >= image->w())) + return 0; + if ((hoty < 0) || (hoty >= image->h())) + return 0; + + memset(&bi, 0, sizeof(BITMAPV5HEADER)); + + bi.bV5Size = sizeof(BITMAPV5HEADER); + bi.bV5Width = image->w(); + bi.bV5Height = image->h(); + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00FF0000; + bi.bV5GreenMask = 0x0000FF00; + bi.bV5BlueMask = 0x000000FF; + bi.bV5AlphaMask = 0xFF000000; + + HDC hdc; + + hdc = GetDC(NULL); + bitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ReleaseDC(NULL, hdc); + + const uchar *i = (const uchar*)*image->data(); + for (int y = 0;y < image->h();y++) { + for (int x = 0;x < image->w();x++) { + switch (image->d()) { + case 1: + *bits = (0xff<<24) | (i[0]<<16) | (i[0]<<8) | i[0]; + break; + case 2: + *bits = (i[1]<<24) | (i[0]<<16) | (i[0]<<8) | i[0]; + break; + case 3: + *bits = (0xff<<24) | (i[0]<<16) | (i[1]<<8) | i[2]; + break; + case 4: + *bits = (i[3]<<24) | (i[0]<<16) | (i[1]<<8) | i[2]; + break; + } + i += image->d(); + bits++; + } + i += image->ld(); + } + + // A mask bitmap is still needed even though it isn't used + mask = CreateBitmap(image->w(),image->h(),1,1,NULL); + + ICONINFO ii; + + ii.fIcon = FALSE; + ii.xHotspot = hotx; + ii.yHotspot = hoty; + ii.hbmMask = mask; + ii.hbmColor = bitmap; + + cursor = CreateIconIndirect(&ii); + + DeleteObject(bitmap); + DeleteObject(mask); + + SetCursor(cursor); + + return 1; +} + +//////////////////////////////////////////////////////////////// // Implement the virtual functions for the base Fl_Window class: // If the box is a filled rectangle, we can make the redisplay *look* diff --git a/common/fltk/src/Fl_x.cxx b/common/fltk/src/Fl_x.cxx index 4ef92b06..8c72dba3 100644 --- a/common/fltk/src/Fl_x.cxx +++ b/common/fltk/src/Fl_x.cxx @@ -52,6 +52,15 @@ # include <X11/Xlocale.h> # include <X11/Xlib.h> # include <X11/keysym.h> +# include <X11/cursorfont.h> + +# if HAVE_XCURSOR +# include <X11/Xcursor/Xcursor.h> +# endif + +# ifdef HAVE_XFIXES +# include <X11/extensions/Xfixes.h> +# endif static Fl_Xlib_Graphics_Driver fl_xlib_driver; static Fl_Display_Device fl_xlib_display(&fl_xlib_driver); @@ -300,14 +309,20 @@ XVisualInfo *fl_visual; Colormap fl_colormap; XIM fl_xim_im = 0; XIC fl_xim_ic = 0; +Window fl_xim_win = 0; char fl_is_over_the_spot = 0; static XRectangle status_area; +static bool have_xfixes = false; +static int xfixes_event_base = 0; static Atom WM_DELETE_WINDOW; static Atom WM_PROTOCOLS; static Atom fl_MOTIF_WM_HINTS; static Atom TARGETS; static Atom CLIPBOARD; +static Atom TIMESTAMP; +static Atom PRIMARY_TIMESTAMP; +static Atom CLIPBOARD_TIMESTAMP; Atom fl_XdndAware; Atom fl_XdndSelection; Atom fl_XdndEnter; @@ -328,6 +343,9 @@ Atom fl_XaUtf8String; Atom fl_XaTextUriList; Atom fl_NET_WM_NAME; // utf8 aware window label Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name +Atom fl_NET_SUPPORTING_WM_CHECK; +Atom fl_NET_WM_STATE; +Atom fl_NET_WM_STATE_FULLSCREEN; /* X defines 32-bit-entities to have a format value of max. 32, @@ -581,6 +599,65 @@ void fl_init_xim() { if(xim_styles) XFree(xim_styles); } +void fl_xim_deactivate(void); + +void fl_xim_activate(Window xid) +{ + if (!fl_xim_im) + return; + + // If the focused window has changed, then use the brute force method + // of completely recreating the input context. + if (fl_xim_win != xid) { + fl_xim_deactivate(); + + fl_new_ic(); + fl_xim_win = xid; + + XSetICValues(fl_xim_ic, + XNFocusWindow, fl_xim_win, + XNClientWindow, fl_xim_win, + NULL); + } + + fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height); +} + +void fl_xim_deactivate(void) +{ + if (!fl_xim_ic) + return; + + XDestroyIC(fl_xim_ic); + fl_xim_ic = NULL; + + fl_xim_win = 0; +} + +extern Fl_Window *fl_xfocus; + +void fl_update_focus(void) +{ + Fl_Widget *focus; + + focus = Fl::grab(); + if (!focus) + focus = Fl::focus(); + if (!focus) + return; + + if (focus->simple_keyboard()) { + fl_xim_deactivate(); + } else { + // fl_xfocus should always be set if something has focus, but let's + // play it safe + if (!fl_xfocus || !fl_xid(fl_xfocus)) + return; + + fl_xim_activate(fl_xid(fl_xfocus)); + } +} + void fl_open_display() { if (fl_display) return; @@ -604,6 +681,9 @@ void fl_open_display(Display* d) { fl_MOTIF_WM_HINTS = XInternAtom(d, "_MOTIF_WM_HINTS", 0); TARGETS = XInternAtom(d, "TARGETS", 0); CLIPBOARD = XInternAtom(d, "CLIPBOARD", 0); + TIMESTAMP = XInternAtom(d, "TIMESTAMP", 0); + PRIMARY_TIMESTAMP = XInternAtom(d, "PRIMARY_TIMESTAMP", 0); + CLIPBOARD_TIMESTAMP = XInternAtom(d, "CLIPBOARD_TIMESTAMP", 0); fl_XdndAware = XInternAtom(d, "XdndAware", 0); fl_XdndSelection = XInternAtom(d, "XdndSelection", 0); fl_XdndEnter = XInternAtom(d, "XdndEnter", 0); @@ -625,6 +705,9 @@ void fl_open_display(Display* d) { fl_XaTextUriList = XInternAtom(d, "text/uri-list", 0); fl_NET_WM_NAME = XInternAtom(d, "_NET_WM_NAME", 0); fl_NET_WM_ICON_NAME = XInternAtom(d, "_NET_WM_ICON_NAME", 0); + fl_NET_SUPPORTING_WM_CHECK = XInternAtom(d, "_NET_SUPPORTING_WM_CHECK", 0); + fl_NET_WM_STATE = XInternAtom(d, "_NET_WM_STATE", 0); + fl_NET_WM_STATE_FULLSCREEN = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", 0); if (sizeof(Atom) < 4) atom_bits = sizeof(Atom) * 8; @@ -646,6 +729,14 @@ void fl_open_display(Display* d) { #if !USE_COLORMAP Fl::visual(FL_RGB); #endif + +#ifdef HAVE_XFIXES + int error_base; + if (XFixesQueryExtension(d, &xfixes_event_base, &error_base)) + have_xfixes = true; + else + have_xfixes = false; +#endif } void fl_close_display() { @@ -768,6 +859,31 @@ void fl_sendClientMessage(Window window, Atom message, XSendEvent(fl_display, window, 0, 0, &e); } + +/* + Get window property value (32 bit format) + Returns zero on success, -1 on error +*/ +static int get_xwinprop(Window wnd, Atom prop, long max_length, + unsigned long *nitems, unsigned long **data) { + Atom actual; + int format; + unsigned long bytes_after; + + if (Success != XGetWindowProperty(fl_display, wnd, prop, 0, max_length, + False, AnyPropertyType, &actual, &format, + nitems, &bytes_after, (unsigned char**)data)) { + return -1; + } + + if (actual == None || format != 32) { + return -1; + } + + return 0; +} + + //////////////////////////////////////////////////////////////// // Code for copying to clipboard and DnD out of the program: @@ -787,6 +903,94 @@ void Fl::copy(const char *stuff, int len, int clipboard) { } //////////////////////////////////////////////////////////////// +// Code for tracking clipboard changes: + +static Time primary_timestamp = -1; +static Time clipboard_timestamp = -1; + +extern bool fl_clipboard_notify_empty(void); +extern void fl_trigger_clipboard_notify(int source); + +static void poll_clipboard_owner(void) { + Window xid; + + // No polling needed with Xfixes + if (have_xfixes) + return; + + // No one is interested, so no point polling + if (fl_clipboard_notify_empty()) + return; + + // We need a window for this to work + if (!Fl::first_window()) + return; + xid = fl_xid(Fl::first_window()); + if (!xid) + return; + + // Request an update of the selection time for both the primary and + // clipboard selections. Magic continues when we get a SelectionNotify. + if (!fl_i_own_selection[0]) + XConvertSelection(fl_display, XA_PRIMARY, TIMESTAMP, PRIMARY_TIMESTAMP, + xid, fl_event_time); + if (!fl_i_own_selection[1]) + XConvertSelection(fl_display, CLIPBOARD, TIMESTAMP, CLIPBOARD_TIMESTAMP, + xid, fl_event_time); +} + +static void clipboard_timeout(void *data) +{ + // No one is interested, so stop polling + if (fl_clipboard_notify_empty()) + return; + + poll_clipboard_owner(); + + Fl::repeat_timeout(0.5, clipboard_timeout); +} + +static void handle_clipboard_timestamp(int clipboard, Time time) +{ + Time *timestamp; + + timestamp = clipboard ? &clipboard_timestamp : &primary_timestamp; + + if (!have_xfixes) { + // Initial scan, just store the value + if (*timestamp == (Time)-1) { + *timestamp = time; + return; + } + } + + // Same selection + if (time == *timestamp) + return; + + *timestamp = time; + + // Something happened! Let's tell someone! + fl_trigger_clipboard_notify(clipboard); +} + +void fl_clipboard_notify_change() { + // Reset the timestamps if we've going idle so that you don't + // get a bogus immediate trigger next time they're activated. + if (fl_clipboard_notify_empty()) { + primary_timestamp = -1; + clipboard_timestamp = -1; + } else { + if (!have_xfixes) { + poll_clipboard_owner(); + + if (!Fl::has_timeout(clipboard_timeout)) + Fl::add_timeout(0.5, clipboard_timeout); + } + } +} + +//////////////////////////////////////////////////////////////// const XEvent* fl_xevent; // the current x event ulong fl_event_time; // the last timestamp from an x event @@ -864,10 +1068,9 @@ int fl_handle(const XEvent& thisevent) XEvent xevent = thisevent; fl_xevent = &thisevent; Window xid = xevent.xany.window; - static Window xim_win = 0; if (fl_xim_ic && xevent.type == DestroyNotify && - xid != xim_win && !fl_find(xid)) + xid != fl_xim_win && !fl_find(xid)) { XIM xim_im; xim_im = XOpenIM(fl_display, NULL, NULL, NULL); @@ -882,49 +1085,11 @@ int fl_handle(const XEvent& thisevent) return 0; } - if (fl_xim_ic && (xevent.type == FocusIn)) - { -#define POOR_XIM -#ifdef POOR_XIM - if (xim_win != xid) - { - xim_win = xid; - XDestroyIC(fl_xim_ic); - fl_xim_ic = NULL; - fl_new_ic(); - XSetICValues(fl_xim_ic, - XNFocusWindow, xevent.xclient.window, - XNClientWindow, xid, - NULL); - } - fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height); -#else - if (Fl::first_window() && Fl::first_window()->modal()) { - Window x = fl_xid(Fl::first_window()); - if (x != xim_win) { - xim_win = x; - XSetICValues(fl_xim_ic, - XNFocusWindow, xim_win, - XNClientWindow, xim_win, - NULL); - fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height); - } - } else if (xim_win != xid && xid) { - xim_win = xid; - XSetICValues(fl_xim_ic, - XNFocusWindow, xevent.xclient.window, - XNClientWindow, xid, - //XNFocusWindow, xim_win, - //XNClientWindow, xim_win, - NULL); - fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height); - } -#endif + if (fl_xim_ic) { + if (XFilterEvent((XEvent *)&xevent, 0)) + return 1; } - if ( XFilterEvent((XEvent *)&xevent, 0) ) - return(1); - switch (xevent.type) { case KeymapNotify: @@ -936,7 +1101,6 @@ int fl_handle(const XEvent& thisevent) return 0; case SelectionNotify: { - if (!fl_selection_requestor) return 0; static unsigned char* buffer = 0; if (buffer) {XFree(buffer); buffer = 0;} long bytesread = 0; @@ -952,6 +1116,19 @@ int fl_handle(const XEvent& thisevent) bytesread/4, 65536, 1, 0, &actual, &format, &count, &remaining, &portion)) break; // quit on error + + if ((fl_xevent->xselection.property == PRIMARY_TIMESTAMP) || + (fl_xevent->xselection.property == CLIPBOARD_TIMESTAMP)) { + if (portion && format == 32 && count == 1) { + Time t = *(unsigned int*)portion; + if (fl_xevent->xselection.property == CLIPBOARD_TIMESTAMP) + handle_clipboard_timestamp(1, t); + else + handle_clipboard_timestamp(0, t); + } + return true; + } + if (actual == TARGETS || actual == XA_ATOM) { Atom type = XA_STRING; for (unsigned i = 0; i<count; i++) { @@ -988,6 +1165,9 @@ int fl_handle(const XEvent& thisevent) buffer[bytesread] = 0; convert_crlf(buffer, bytesread); } + + if (!fl_selection_requestor) return 0; + Fl::e_text = buffer ? (char*)buffer : (char *)""; Fl::e_length = bytesread; int old_event = Fl::e_number; @@ -1008,6 +1188,7 @@ int fl_handle(const XEvent& thisevent) case SelectionClear: { int clipboard = fl_xevent->xselectionclear.selection == CLIPBOARD; fl_i_own_selection[clipboard] = 0; + poll_clipboard_owner(); return 1;} case SelectionRequest: { @@ -1220,6 +1401,9 @@ int fl_handle(const XEvent& thisevent) case FocusIn: if (fl_xim_ic) XSetICFocus(fl_xim_ic); event = FL_FOCUS; + // If the user has toggled from another application to this one, + // then it's a good time to check for clipboard changes. + poll_clipboard_owner(); break; case FocusOut: @@ -1260,15 +1444,15 @@ int fl_handle(const XEvent& thisevent) //static XComposeStatus compose; len = XLookupString((XKeyEvent*)&(xevent.xkey), buffer, buffer_len, &keysym, 0/*&compose*/); - if (keysym && keysym < 0x400) { // a character in latin-1,2,3,4 sets - // force it to type a character (not sure if this ever is needed): - // if (!len) {buffer[0] = char(keysym); len = 1;} - len = fl_utf8encode(XKeysymToUcs(keysym), buffer); - if (len < 1) len = 1; - // ignore all effects of shift on the keysyms, which makes it a lot - // easier to program shortcuts and is Windoze-compatible: - keysym = XKeycodeToKeysym(fl_display, keycode, 0); - } + // XLookupString() is only defined to return Latin-1 (although it + // often gives you more). To be safe, use our own lookups based on + // keysym. + len = fl_utf8encode(XKeysymToUcs(keysym), buffer); + if (len < 1) + len = 1; + // ignore all effects of shift on the keysyms, which makes it a lot + // easier to program shortcuts and is Windoze-compatable: + keysym = XKeycodeToKeysym(fl_display, keycode, 0); } // MRS: Can't use Fl::event_state(FL_CTRL) since the state is not // set until set_event_xy() is called later... @@ -1440,12 +1624,19 @@ int fl_handle(const XEvent& thisevent) case ButtonPress: Fl::e_keysym = FL_Button + xevent.xbutton.button; set_event_xy(); + Fl::e_dx = Fl::e_dy = 0; if (xevent.xbutton.button == Button4) { Fl::e_dy = -1; // Up event = FL_MOUSEWHEEL; } else if (xevent.xbutton.button == Button5) { Fl::e_dy = +1; // Down event = FL_MOUSEWHEEL; + } else if (xevent.xbutton.button == 6) { + Fl::e_dx = -1; // Left + event = FL_MOUSEWHEEL; + } else if (xevent.xbutton.button == 7) { + Fl::e_dx = +1; // Right + event = FL_MOUSEWHEEL; } else { Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1)); event = FL_PUSH; @@ -1456,6 +1647,31 @@ int fl_handle(const XEvent& thisevent) in_a_window = true; break; + case PropertyNotify: + if (xevent.xproperty.atom == fl_NET_WM_STATE) { + int fullscreen_state = 0; + if (xevent.xproperty.state != PropertyDelete) { + unsigned long nitems; + unsigned long *words = 0; + if (0 == get_xwinprop(xid, fl_NET_WM_STATE, 64, &nitems, &words) ) { + for (unsigned long item = 0; item < nitems; item++) { + if (words[item] == fl_NET_WM_STATE_FULLSCREEN) { + fullscreen_state = 1; + } + } + } + } + if (window->fullscreen_active() && !fullscreen_state) { + window->_clear_fullscreen(); + event = FL_FULLSCREEN; + } + if (!window->fullscreen_active() && fullscreen_state) { + window->_set_fullscreen(); + event = FL_FULLSCREEN; + } + } + break; + case MotionNotify: set_event_xy(); # if CONSOLIDATE_MOTION @@ -1556,6 +1772,25 @@ int fl_handle(const XEvent& thisevent) } } +#ifdef HAVE_XFIXES + switch (xevent.type - xfixes_event_base) { + case XFixesSelectionNotify: { + // Someone feeding us bogus events? + if (!have_xfixes) + return true; + + XFixesSelectionNotifyEvent *selection_notify = (XFixesSelectionNotifyEvent *)&xevent; + + if ((selection_notify->selection == XA_PRIMARY) && !fl_i_own_selection[0]) + handle_clipboard_timestamp(0, selection_notify->selection_timestamp); + else if ((selection_notify->selection == CLIPBOARD) && !fl_i_own_selection[1]) + handle_clipboard_timestamp(1, selection_notify->selection_timestamp); + + return true; + } + } +#endif + return Fl::handle(event, window); } @@ -1595,6 +1830,75 @@ void Fl_Window::resize(int X,int Y,int W,int H) { //////////////////////////////////////////////////////////////// +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ + +static void send_wm_state_event(Window wnd, int add, Atom prop) { + XEvent e; + e.xany.type = ClientMessage; + e.xany.window = wnd; + e.xclient.message_type = fl_NET_WM_STATE; + e.xclient.format = 32; + e.xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + e.xclient.data.l[1] = prop; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + XSendEvent(fl_display, RootWindow(fl_display, fl_screen), + 0, SubstructureNotifyMask | SubstructureRedirectMask, + &e); +} + +int ewmh_supported() { + static int result = -1; + + if (result == -1) { + result = 0; + unsigned long nitems; + unsigned long *words = 0; + if (0 == get_xwinprop(XRootWindow(fl_display, fl_screen), fl_NET_SUPPORTING_WM_CHECK, 64, + &nitems, &words) && nitems == 1) { + Window child = words[0]; + if (0 == get_xwinprop(child, fl_NET_SUPPORTING_WM_CHECK, 64, + &nitems, &words) && nitems == 1) { + result = (child == words[0]); + } + } + } + + return result; +} + +/* Change an existing window to fullscreen */ +void fullscreen_x(Fl_Window *w) { + if (ewmh_supported()) { + send_wm_state_event(fl_xid(w), 1, fl_NET_WM_STATE_FULLSCREEN); + } else { + w->_set_fullscreen(); + w->hide(); + w->show(); + /* We want to grab the window, not a widget, so we cannot use Fl::grab */ + XGrabKeyboard(fl_display, fl_xid(w), 1, GrabModeAsync, GrabModeAsync, fl_event_time); + Fl::handle(FL_FULLSCREEN, w); + } +} + +void fullscreen_off_x(Fl_Window *w, int X, int Y, int W, int H) { + if (ewmh_supported()) { + send_wm_state_event(fl_xid(w), 0, fl_NET_WM_STATE_FULLSCREEN); + } else { + w->_clear_fullscreen(); + /* The grab will be lost when the window is destroyed */ + w->hide(); + w->resize(X,Y,W,H); + w->show(); + Fl::handle(FL_FULLSCREEN, w); + } +} + +//////////////////////////////////////////////////////////////// + // A subclass of Fl_Window may call this to associate an X window it // creates with the Fl_Window: @@ -1630,6 +1934,7 @@ ExposureMask|StructureNotifyMask |KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask |ButtonPressMask|ButtonReleaseMask |EnterWindowMask|LeaveWindowMask +|PropertyChangeMask |PointerMotionMask; void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) @@ -1701,6 +2006,16 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) attr.save_under = 1; mask |= CWSaveUnder; if (!win->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;} } + // For the non-EWMH fullscreen case, we cannot use the code above, + // since we do not want save_under, do not want to turn off the + // border, and cannot grab without an existing window. Besides, + // there is no clear_override(). + if (win->flags() & Fl_Widget::FULLSCREEN && !ewmh_supported()) { + attr.override_redirect = 1; + mask |= CWOverrideRedirect; + Fl::screen_xywh(X, Y, W, H, X, Y, W, H); + } + if (fl_background_pixel >= 0) { attr.background_pixel = fl_background_pixel; fl_background_pixel = -1; @@ -1760,6 +2075,12 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) PropModeAppend, (unsigned char*) &net_wm_state_skip_taskbar, 1); } + // If asked for, create fullscreen + if (win->flags() & Fl_Widget::FULLSCREEN && ewmh_supported()) { + XChangeProperty (fl_display, xp->xid, fl_NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, (unsigned char*) &fl_NET_WM_STATE_FULLSCREEN, 1); + } + // Make it receptive to DnD: long version = 4; XChangeProperty(fl_display, xp->xid, fl_XdndAware, @@ -1789,6 +2110,16 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) XChangeProperty(fl_display, xp->xid, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_type_kind, 1); } +#ifdef HAVE_XFIXES + // register for clipboard change notifications + if (have_xfixes && !win->parent()) { + XFixesSelectSelectionInput(fl_display, xp->xid, XA_PRIMARY, + XFixesSetSelectionOwnerNotifyMask); + XFixesSelectSelectionInput(fl_display, xp->xid, CLIPBOARD, + XFixesSetSelectionOwnerNotifyMask); + } +#endif + XMapWindow(fl_display, xp->xid); if (showit) { win->set_visible(); @@ -1797,6 +2128,12 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) Fl::e_number = old_event; win->redraw(); } + + // non-EWMH fullscreen case, need grab + if (win->flags() & Fl_Widget::FULLSCREEN && !ewmh_supported()) { + XGrabKeyboard(fl_display, xp->xid, 1, GrabModeAsync, GrabModeAsync, fl_event_time); + } + } //////////////////////////////////////////////////////////////// @@ -1883,6 +2220,94 @@ void Fl_Window::size_range_() { //////////////////////////////////////////////////////////////// +int Fl_X::set_cursor(Fl_Cursor c) { + unsigned int shape; + Cursor xc; + + switch (c) { + case FL_CURSOR_ARROW: shape = XC_left_ptr; break; + case FL_CURSOR_CROSS: shape = XC_tcross; break; + case FL_CURSOR_WAIT: shape = XC_watch; break; + case FL_CURSOR_INSERT: shape = XC_xterm; break; + case FL_CURSOR_HAND: shape = XC_hand2; break; + case FL_CURSOR_HELP: shape = XC_question_arrow; break; + case FL_CURSOR_MOVE: shape = XC_fleur; break; + case FL_CURSOR_NS: shape = XC_sb_v_double_arrow; break; + case FL_CURSOR_WE: shape = XC_sb_h_double_arrow; break; + case FL_CURSOR_NE: shape = XC_top_right_corner; break; + case FL_CURSOR_N: shape = XC_top_side; break; + case FL_CURSOR_NW: shape = XC_top_left_corner; break; + case FL_CURSOR_E: shape = XC_right_side; break; + case FL_CURSOR_W: shape = XC_left_side; break; + case FL_CURSOR_SE: shape = XC_bottom_right_corner; break; + case FL_CURSOR_S: shape = XC_bottom_side; break; + case FL_CURSOR_SW: shape = XC_bottom_left_corner; break; + default: + return 0; + } + + xc = XCreateFontCursor(fl_display, shape); + XDefineCursor(fl_display, xid, xc); + XFreeCursor(fl_display, xc); + + return 1; +} + +int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { +#if ! HAVE_XCURSOR + return 0; +#else + XcursorImage *cursor; + Cursor xc; + + if ((hotx < 0) || (hotx >= image->w())) + return 0; + if ((hoty < 0) || (hoty >= image->h())) + return 0; + + cursor = XcursorImageCreate(image->w(), image->h()); + if (!cursor) + return 0; + + const uchar *i = (const uchar*)*image->data(); + XcursorPixel *o = cursor->pixels; + for (int y = 0;y < image->h();y++) { + for (int x = 0;x < image->w();x++) { + switch (image->d()) { + case 1: + *o = (0xff<<24) | (i[0]<<16) | (i[0]<<8) | i[0]; + break; + case 2: + *o = (i[1]<<24) | (i[0]<<16) | (i[0]<<8) | i[0]; + break; + case 3: + *o = (0xff<<24) | (i[0]<<16) | (i[1]<<8) | i[2]; + break; + case 4: + *o = (i[3]<<24) | (i[0]<<16) | (i[1]<<8) | i[2]; + break; + } + i += image->d(); + o++; + } + i += image->ld(); + } + + cursor->xhot = hotx; + cursor->yhot = hoty; + + xc = XcursorImageLoadCursor(fl_display, cursor); + XDefineCursor(fl_display, xid, xc); + XFreeCursor(fl_display, xc); + + XcursorImageDestroy(cursor); + + return 1; +#endif +} + +//////////////////////////////////////////////////////////////// + // returns pointer to the filename, or null if name ends with '/' const char *fl_filename_name(const char *name) { const char *p,*q; diff --git a/common/fltk/src/fl_cursor.cxx b/common/fltk/src/fl_cursor.cxx index 66492551..bad3936b 100644 --- a/common/fltk/src/fl_cursor.cxx +++ b/common/fltk/src/fl_cursor.cxx @@ -33,300 +33,155 @@ #include <FL/Fl.H> #include <FL/Fl_Window.H> +#include <FL/Fl_Pixmap.H> +#include <FL/Fl_RGB_Image.H> #include <FL/x.H> -#if !defined(WIN32) && !defined(__APPLE__) -# include <X11/cursorfont.h> -#endif #include <FL/fl_draw.H> +#include "fl_cursor_wait.xpm" +#include "fl_cursor_help.xpm" +#include "fl_cursor_nwse.xpm" +#include "fl_cursor_nesw.xpm" +#include "fl_cursor_none.xpm" + /** Sets the cursor for the current window to the specified shape and colors. The cursors are defined in the <FL/Enumerations.H> header file. */ +void fl_cursor(Fl_Cursor c) { + if (Fl::first_window()) Fl::first_window()->cursor(c); +} + +/* For back compatibility only. */ void fl_cursor(Fl_Cursor c, Fl_Color fg, Fl_Color bg) { - if (Fl::first_window()) Fl::first_window()->cursor(c,fg,bg); + fl_cursor(c); } + + /** - Sets the default window cursor as well as its color. + Sets the default window cursor. This is the cursor that will be used + after the mouse pointer leaves a widget with a custom cursor set. - For back compatibility only. + \see cursor(const Fl_RGB_Image*, int, int), default_cursor() */ -void Fl_Window::default_cursor(Fl_Cursor c, Fl_Color fg, Fl_Color bg) { -// if (c == FL_CURSOR_DEFAULT) c = FL_CURSOR_ARROW; - +void Fl_Window::default_cursor(Fl_Cursor c) { cursor_default = c; - cursor_fg = fg; - cursor_bg = bg; + cursor(c); +} - cursor(c, fg, bg); + +void fallback_cursor(Fl_Window *w, Fl_Cursor c) { + const char **xpm; + int hotx, hoty; + + // The standard arrow is our final fallback, so something is broken + // if we get called back here with that as an argument. + if (c == FL_CURSOR_ARROW) + return; + + switch (c) { + case FL_CURSOR_WAIT: + xpm = (const char**)fl_cursor_wait_xpm; + hotx = 8; + hoty = 15; + break; + case FL_CURSOR_HELP: + xpm = (const char**)fl_cursor_help_xpm; + hotx = 1; + hoty = 3; + break; + case FL_CURSOR_NWSE: + xpm = (const char**)fl_cursor_nwse_xpm; + hotx = 7; + hoty = 7; + break; + case FL_CURSOR_NESW: + xpm = (const char**)fl_cursor_nesw_xpm; + hotx = 7; + hoty = 7; + break; + case FL_CURSOR_NONE: + xpm = (const char**)fl_cursor_none_xpm; + hotx = 0; + hoty = 0; + break; + default: + w->cursor(FL_CURSOR_ARROW); + return; + } + + Fl_Pixmap pxm(xpm); + Fl_RGB_Image image(&pxm); + + w->cursor(&image, hotx, hoty); } -#ifdef WIN32 -# ifndef IDC_HAND -# define IDC_HAND MAKEINTRESOURCE(32649) -# endif // !IDC_HAND +void Fl_Window::cursor(Fl_Cursor c) { + int ret; -void Fl_Window::cursor(Fl_Cursor c, Fl_Color c1, Fl_Color c2) { - if (!shown()) return; // the cursor must be set for the top level window, not for subwindows Fl_Window *w = window(), *toplevel = this; - while (w) { toplevel = w; w = w->window(); } - if (toplevel != this) { toplevel->cursor(c, c1, c2); return; } - // now set the actual cursor - if (c == FL_CURSOR_DEFAULT) { - c = cursor_default; + + while (w) { + toplevel = w; + w = w->window(); } - if (c > FL_CURSOR_NESW) { - i->cursor = 0; - } else if (c == FL_CURSOR_DEFAULT) { - i->cursor = fl_default_cursor; - } else { - LPSTR n; - switch (c) { - case FL_CURSOR_ARROW: n = IDC_ARROW; break; - case FL_CURSOR_CROSS: n = IDC_CROSS; break; - case FL_CURSOR_WAIT: n = IDC_WAIT; break; - case FL_CURSOR_INSERT: n = IDC_IBEAM; break; - case FL_CURSOR_HELP: n = IDC_HELP; break; - case FL_CURSOR_HAND: { - OSVERSIONINFO osvi; - - // Get the OS version: Windows 98 and 2000 have a standard - // hand cursor. - memset(&osvi, 0, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osvi); - - if (osvi.dwMajorVersion > 4 || - (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion > 0 && - osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)) n = IDC_HAND; - else n = IDC_UPARROW; - } break; - case FL_CURSOR_MOVE: n = IDC_SIZEALL; break; - case FL_CURSOR_N: - case FL_CURSOR_S: - case FL_CURSOR_NS: n = IDC_SIZENS; break; - case FL_CURSOR_NE: - case FL_CURSOR_SW: - case FL_CURSOR_NESW: n = IDC_SIZENESW; break; - case FL_CURSOR_E: - case FL_CURSOR_W: - case FL_CURSOR_WE: n = IDC_SIZEWE; break; - case FL_CURSOR_SE: - case FL_CURSOR_NW: - case FL_CURSOR_NWSE: n = IDC_SIZENWSE; break; - default: n = IDC_NO; break; - } - i->cursor = LoadCursor(NULL, n); + + if (toplevel != this) { + toplevel->cursor(c); + return; } - SetCursor(i->cursor); -} -#elif defined(__APPLE__) - -#ifdef __BIG_ENDIAN__ -# define E(x) x -#elif defined __LITTLE_ENDIAN__ -// Don't worry. This will be resolved at compile time -# define E(x) (x>>8)|((x<<8)&0xff00) -#else -# error "Either __LITTLE_ENDIAN__ or __BIG_ENDIAN__ must be defined" -#endif - -extern Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h); - - -CGContextRef Fl_X::help_cursor_image(void) -{ - int w = 20, h = 20; - Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); - fl_begin_offscreen(off); - CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); - fl_rectf(0,0,w,h); - fl_color(FL_BLACK); - fl_font(FL_COURIER_BOLD, 20); - fl_draw("?", 1, h-1); - fl_end_offscreen(); - return (CGContextRef)off; -} + if (c == FL_CURSOR_DEFAULT) + c = cursor_default; -CGContextRef Fl_X::none_cursor_image(void) -{ - int w = 20, h = 20; - Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); - fl_begin_offscreen(off); - CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); - fl_rectf(0,0,w,h); - fl_end_offscreen(); - return (CGContextRef)off; -} + if (!i) + return; -CGContextRef Fl_X::watch_cursor_image(void) -{ - int w, h, r = 5; - w = 2*r+6; - h = 4*r; - Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); - fl_begin_offscreen(off); - CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); - fl_rectf(0,0,w,h); - CGContextTranslateCTM( (CGContextRef)off, w/2, h/2); - fl_color(FL_WHITE); - fl_circle(0, 0, r+1); - fl_color(FL_BLACK); - fl_rectf(int(-r*0.7), int(-r*1.7), int(1.4*r), int(3.4*r)); - fl_rectf(r-1, -1, 3, 3); - fl_color(FL_WHITE); - fl_pie(-r, -r, 2*r, 2*r, 0, 360); - fl_color(FL_BLACK); - fl_circle(0,0,r); - fl_xyline(0, 0, int(-r*.7)); - fl_xyline(0, 0, 0, int(-r*.7)); - fl_end_offscreen(); - return (CGContextRef)off; -} + ret = i->set_cursor(c); + if (ret) + return; -CGContextRef Fl_X::nesw_cursor_image(void) -{ - int c = 7, r = 2*c; - int w = r, h = r; - Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); - fl_begin_offscreen(off); - CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); - fl_rectf(0,0,w,h); - CGContextTranslateCTM( (CGContextRef)off, 0, h); - CGContextScaleCTM( (CGContextRef)off, 1, -1); - fl_color(FL_BLACK); - fl_polygon(0, 0, c, 0, 0, c); - fl_polygon(r, r, r, r-c, r-c, r); - fl_line_style(FL_SOLID, 2, 0); - fl_line(0,1, r,r+1); - fl_line_style(FL_SOLID, 0, 0); - fl_end_offscreen(); - return (CGContextRef)off; + fallback_cursor(this, c); } -CGContextRef Fl_X::nwse_cursor_image(void) -{ - int c = 7, r = 2*c; - int w = r, h = r; - Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); - fl_begin_offscreen(off); - CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); - fl_rectf(0,0,w,h); - CGContextTranslateCTM( (CGContextRef)off, 0, h); - CGContextScaleCTM( (CGContextRef)off, 1, -1); - fl_color(FL_BLACK); - fl_polygon(r-1, 0, r-1, c, r-1-c, 0); - fl_polygon(-1, r, c-1, r, -1, r-c); - fl_line_style(FL_SOLID, 2, 0); - fl_line(r-1,1, -1,r+1); - fl_line_style(FL_SOLID, 0, 0); - fl_end_offscreen(); - return (CGContextRef)off; -} +/** + Changes the cursor for this window. This always calls the system, if + you are changing the cursor a lot you may want to keep track of how + you set it in a static variable and call this only if the new cursor + is different. -void Fl_Window::cursor(Fl_Cursor c, Fl_Color, Fl_Color) { - if (c == FL_CURSOR_DEFAULT) { - c = cursor_default; - } - if (i) i->set_cursor(c); -} + The default cursor will be used if the provided image cannot be used + as a cursor. + + \see cursor(Fl_Cursor), default_cursor() +*/ +void Fl_Window::cursor(const Fl_RGB_Image *image, int hotx, int hoty) { + int ret; + + // the cursor must be set for the top level window, not for subwindows + Fl_Window *w = window(), *toplevel = this; -#else - -// I like the MSWindows resize cursors, so I duplicate them here: - -#define CURSORSIZE 16 -#define HOTXY 7 -static struct TableEntry { - uchar bits[CURSORSIZE*CURSORSIZE/8]; - uchar mask[CURSORSIZE*CURSORSIZE/8]; - Cursor cursor; -} table[] = { - {{ // FL_CURSOR_NS - 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0x80, 0x01, 0x80, 0x01, - 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, - 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00}, - { - 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, 0xc0, 0x03, - 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, - 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01}}, - {{ // FL_CURSOR_EW - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, - 0x0c, 0x30, 0xfe, 0x7f, 0xfe, 0x7f, 0x0c, 0x30, 0x08, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x1c, 0x38, - 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0x1c, 0x38, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{ // FL_CURSOR_NWSE - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x38, 0x00, 0x78, 0x00, - 0xe8, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - { - 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x7c, 0x00, 0xfc, 0x00, - 0xfc, 0x01, 0xec, 0x03, 0xc0, 0x37, 0x80, 0x3f, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00}}, - {{ // FL_CURSOR_NESW - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1e, - 0x00, 0x17, 0x80, 0x03, 0xc0, 0x01, 0xe8, 0x00, 0x78, 0x00, 0x38, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3f, - 0x80, 0x3f, 0xc0, 0x37, 0xec, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0x7c, 0x00, - 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0}, {0}} // FL_CURSOR_NONE & unknown -}; - -void Fl_Window::cursor(Fl_Cursor c, Fl_Color fg, Fl_Color bg) { - if (!shown()) return; - Cursor xc; - int deleteit = 0; - if (c == FL_CURSOR_DEFAULT) { - c = cursor_default; - fg = cursor_fg; - bg = cursor_bg; + while (w) { + toplevel = w; + w = w->window(); } - if (!c) { - xc = None; - } else { - if (c >= FL_CURSOR_NS) { - TableEntry *q = (c > FL_CURSOR_NESW) ? table+4 : table+(c-FL_CURSOR_NS); - if (!(q->cursor)) { - XColor dummy = { 0 }; - Pixmap p = XCreateBitmapFromData(fl_display, - RootWindow(fl_display, fl_screen), (const char*)(q->bits), - CURSORSIZE, CURSORSIZE); - Pixmap m = XCreateBitmapFromData(fl_display, - RootWindow(fl_display, fl_screen), (const char*)(q->mask), - CURSORSIZE, CURSORSIZE); - q->cursor = XCreatePixmapCursor(fl_display, p,m,&dummy, &dummy, - HOTXY, HOTXY); - XFreePixmap(fl_display, m); - XFreePixmap(fl_display, p); - } - xc = q->cursor; - } else { - xc = XCreateFontCursor(fl_display, (c-1)*2); - deleteit = 1; - } - XColor fgc; - uchar r,g,b; - Fl::get_color(fg,r,g,b); - fgc.red = r<<8; fgc.green = g<<8; fgc.blue = b<<8; - XColor bgc; - Fl::get_color(bg,r,g,b); - bgc.red = r<<8; bgc.green = g<<8; bgc.blue = b<<8; - XRecolorCursor(fl_display, xc, &fgc, &bgc); + if (toplevel != this) { + toplevel->cursor(image, hotx, hoty); + return; } - XDefineCursor(fl_display, fl_xid(this), xc); - if (deleteit) XFreeCursor(fl_display, xc); -} -#endif + if (!i) + return; + + ret = i->set_cursor(image, hotx, hoty); + if (ret) + return; + + cursor(FL_CURSOR_DEFAULT); +} // // End of "$Id: fl_cursor.cxx 8055 2010-12-18 22:31:01Z manolo $". diff --git a/common/fltk/src/fl_cursor_help.xpm b/common/fltk/src/fl_cursor_help.xpm new file mode 100644 index 00000000..2d5b2574 --- /dev/null +++ b/common/fltk/src/fl_cursor_help.xpm @@ -0,0 +1,95 @@ +/* XPM */ +static const char * fl_cursor_help_xpm[] = { +"16 27 65 1", +" c None", +". c #FFFFFF", +"+ c #E2E2E2", +"@ c #1C1C1C", +"# c #E7E7E7", +"$ c #000000", +"% c #212121", +"& c #EAEAEA", +"* c #262626", +"= c #EDEDED", +"- c #2C2C2C", +"; c #F0F0F0", +"> c #333333", +", c #F1F1F1", +"' c #393939", +") c #F3F3F3", +"! c #404040", +"~ c #484848", +"{ c #F4F4F4", +"] c #050505", +"^ c #202020", +"/ c #707070", +"( c #F5F5F5", +"_ c #040404", +": c #E1E1E1", +"< c #EEEEEE", +"[ c #EFEFEF", +"} c #FEFEFE", +"| c #3D3D3D", +"1 c #7E7E7E", +"2 c #696969", +"3 c #414141", +"4 c #131313", +"5 c #080808", +"6 c #454545", +"7 c #F2F2F2", +"8 c #878787", +"9 c #7D7D7D", +"0 c #101010", +"a c #111111", +"b c #FDFDFD", +"c c #8A8A8A", +"d c #E6E6E6", +"e c #7B7B7B", +"f c #4C4C4C", +"g c #5C5C5C", +"h c #9F9F9F", +"i c #F9F9F9", +"j c #F7F7F7", +"k c #B1B1B1", +"l c #2E2E2E", +"m c #767676", +"n c #DCDCDC", +"o c #DEDEDE", +"p c #C7C7C7", +"q c #1B1B1B", +"r c #6B6B6B", +"s c #575757", +"t c #797979", +"u c #020202", +"v c #010101", +"w c #FBFBFB", +"x c #D7D7D7", +"y c #D8D8D8", +"z c #060606", +" ", +". ", +".+ ", +".@# ", +".$%& ", +".$$*= ", +".$$$-; ", +".$$$$>, ", +".$$$$$') ", +".$$$$$$!) ", +".$$$$$$$~{ ", +".$$$$]^^^/( ", +".$$$$_:(<<[} ", +".$$|1$2< ", +".$3,(45[ ", +".67 78$9, ", +".7 {0a( .... ", +"b ,c5[defgh, ", +" )ijk_la$m.", +" no.p$q.", +" .r$s.", +" .t$-= ", +" 7uv+ ", +" wxy. ", +" :$z. ", +" :$z. ", +" .... "}; diff --git a/common/fltk/src/fl_cursor_nesw.xpm b/common/fltk/src/fl_cursor_nesw.xpm new file mode 100644 index 00000000..5a7113a9 --- /dev/null +++ b/common/fltk/src/fl_cursor_nesw.xpm @@ -0,0 +1,46 @@ +/* XPM */ +static const char * fl_cursor_nesw_xpm[] = { +"15 15 28 1", +" c None", +". c #FFFFFF", +"+ c #767676", +"@ c #000000", +"# c #4E4E4E", +"$ c #0C0C0C", +"% c #494949", +"& c #4D4D4D", +"* c #1B1B1B", +"= c #515151", +"- c #646464", +"; c #363636", +"> c #6A6A6A", +", c #545454", +"' c #585858", +") c #242424", +"! c #797979", +"~ c #2E2E2E", +"{ c #444444", +"] c #3B3B3B", +"^ c #0A0A0A", +"/ c #595959", +"( c #F7F7F7", +"_ c #080808", +": c #6B6B6B", +"< c #FDFDFD", +"[ c #FCFCFC", +"} c #FEFEFE", +" ..........", +" .+@@@@@@.", +" .#@@@@@.", +" .$@@@@.", +" .%@@@@@.", +". .&@@@*@@.", +".. .=@@@-.;@.", +".>. .,@@@'. .).", +".@!.'@@@#. ..", +".@@~@@@{. .", +".@@@@@]. ", +".@@@@^. ", +".@@@@@/( ", +".______:( ", +"<[[[[[[[[} "}; diff --git a/common/fltk/src/fl_cursor_none.xpm b/common/fltk/src/fl_cursor_none.xpm new file mode 100644 index 00000000..054e461d --- /dev/null +++ b/common/fltk/src/fl_cursor_none.xpm @@ -0,0 +1,19 @@ +/* XPM */ +static const char * fl_cursor_none_xpm[] = { +"15 15 1 1", +" c None", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/common/fltk/src/fl_cursor_nwse.xpm b/common/fltk/src/fl_cursor_nwse.xpm new file mode 100644 index 00000000..d495eb4f --- /dev/null +++ b/common/fltk/src/fl_cursor_nwse.xpm @@ -0,0 +1,46 @@ +/* XPM */ +static const char * fl_cursor_nwse_xpm[] = { +"15 15 28 1", +" c None", +". c #FFFFFF", +"+ c #000000", +"@ c #767676", +"# c #4E4E4E", +"$ c #0C0C0C", +"% c #494949", +"& c #1B1B1B", +"* c #4D4D4D", +"= c #363636", +"- c #646464", +"; c #515151", +"> c #242424", +", c #585858", +"' c #545454", +") c #6A6A6A", +"! c #797979", +"~ c #444444", +"{ c #2E2E2E", +"] c #3B3B3B", +"^ c #0A0A0A", +"/ c #F7F7F7", +"( c #595959", +"_ c #6B6B6B", +": c #080808", +"< c #FEFEFE", +"[ c #FCFCFC", +"} c #FDFDFD", +".......... ", +".++++++@. ", +".+++++#. ", +".++++$. ", +".+++++%. ", +".++&+++*. .", +".+=.-+++;. ..", +".>. .,+++'. .).", +".. .#+++,.!+.", +". .~+++{++.", +" .]+++++.", +" .^++++.", +" /(+++++.", +" /_::::::.", +" <[[[[[[[[}"}; diff --git a/common/fltk/src/fl_cursor_wait.xpm b/common/fltk/src/fl_cursor_wait.xpm new file mode 100644 index 00000000..400d44d1 --- /dev/null +++ b/common/fltk/src/fl_cursor_wait.xpm @@ -0,0 +1,72 @@ +/* XPM */ +static const char * fl_cursor_wait_xpm[] = { +"17 32 37 1", +" c None", +". c #FFFFFF", +"+ c #2E2E2E", +"@ c #202020", +"# c #F1F1F1", +"$ c #2D2D2D", +"% c #000000", +"& c #EDEDED", +"* c #585858", +"= c #575757", +"- c #FBFBFB", +"; c #848484", +"> c #B8B8B8", +", c #E5E5E5", +"' c #F7F7F7", +") c #181818", +"! c #F0F0F0", +"~ c #616161", +"{ c #B7B7B7", +"] c #F5F5F5", +"^ c #050505", +"/ c #D4D4D4", +"( c #EEEEEE", +"_ c #595959", +": c #7B7B7B", +"< c #E9E9E9", +"[ c #131313", +"} c #E3E3E3", +"| c #767676", +"1 c #505050", +"2 c #F3F3F3", +"3 c #2A2A2A", +"4 c #070707", +"5 c #343434", +"6 c #939393", +"7 c #191919", +"8 c #6A6A6A", +".................", +".+@@@@@@@@@@@@@+.", +".................", +" #$%%%%%%%%%%%$# ", +" &*%%%%%%%%%%%=& ", +" -;%%%%%%%%%%%;- ", +" >%%%%%%%%%%%> ", +" ,%%%%%%%%%%%, ", +" ')%%%%%%%%%)' ", +" !~%%%%%%%%%~! ", +" {%%%%%%%%%{ ", +" ]^/...../^] ", +" (_:.....:_( ", +" <[}...}[< ", +" !|1...1|! ", +" 2[3.3[2 ", +" 2[%.%[2 ", +" !|%%.%%|! ", +" <4%%.%%4< ", +" (_%%%.%%%_( ", +" ]^%%%.%%%^] ", +" {%%%%.%%%%{ ", +" !~%%%%.%%%%~! ", +" ')%%%%.%%%%)' ", +" ,%%56{.{65%%, ", +" >%*.......*%> ", +" -;7&.......&7;- ", +" &*8.........8=& ", +" #$%%%%%%%%%%%$# ", +".................", +".+@@@@@@@@@@@@@+.", +"................."}; diff --git a/common/fltk/src/fl_draw_pixmap.cxx b/common/fltk/src/fl_draw_pixmap.cxx index 68f7df6f..b0244859 100644 --- a/common/fltk/src/fl_draw_pixmap.cxx +++ b/common/fltk/src/fl_draw_pixmap.cxx @@ -67,99 +67,6 @@ int fl_measure_pixmap(const char * const *cdata, int &w, int &h) { return 1; } -#ifdef U64 - -// The callback from fl_draw_image to get a row of data passes this: -struct pixmap_data { - int w, h; - const uchar*const* data; - union { - U64 colors[256]; - U64* byte1[256]; - }; -}; - -// callback for 1 byte per pixel: -static void cb1(void*v, int x, int y, int w, uchar* buf) { - pixmap_data& d = *(pixmap_data*)v; - const uchar* p = d.data[y]+x; - U64* q = (U64*)buf; - for (int X=w; X>0; X-=2, p += 2) { - if (X>1) { -# if WORDS_BIGENDIAN - *q++ = (d.colors[p[0]]<<32) | d.colors[p[1]]; -# else - *q++ = (d.colors[p[1]]<<32) | d.colors[p[0]]; -# endif - } else { -# if WORDS_BIGENDIAN - *q++ = d.colors[p[0]]<<32; -# else - *q++ = d.colors[p[0]]; -# endif - } - } -} - -// callback for 2 bytes per pixel: -static void cb2(void*v, int x, int y, int w, uchar* buf) { - pixmap_data& d = *(pixmap_data*)v; - const uchar* p = d.data[y]+2*x; - U64* q = (U64*)buf; - for (int X=w; X>0; X-=2) { - U64* colors = d.byte1[*p++]; - int index = *p++; - if (X>1) { - U64* colors1 = d.byte1[*p++]; - int index1 = *p++; -# if WORDS_BIGENDIAN - *q++ = (colors[index]<<32) | colors1[index1]; -# else - *q++ = (colors1[index1]<<32) | colors[index]; -# endif - } else { -# if WORDS_BIGENDIAN - *q++ = colors[index]<<32; -# else - *q++ = colors[index]; -# endif - } - } -} - -#else // U32 - -// The callback from fl_draw_image to get a row of data passes this: -struct pixmap_data { - int w, h; - const uchar*const* data; - union { - U32 colors[256]; - U32* byte1[256]; - }; -}; - -// callback for 1 byte per pixel: -static void cb1(void*v, int x, int y, int w, uchar* buf) { - pixmap_data& d = *(pixmap_data*)v; - const uchar* p = d.data[y]+x; - U32* q = (U32*)buf; - for (int X=w; X--;) *q++ = d.colors[*p++]; -} - -// callback for 2 bytes per pixel: -static void cb2(void*v, int x, int y, int w, uchar* buf) { - pixmap_data& d = *(pixmap_data*)v; - const uchar* p = d.data[y]+2*x; - U32* q = (U32*)buf; - for (int X=w; X--;) { - U32* colors = d.byte1[*p++]; - *q++ = colors[*p++]; - } -} - -#endif // U64 else U32 - uchar **fl_mask_bitmap; // if non-zero, create bitmap and store pointer here /** @@ -209,34 +116,33 @@ static void make_unused_color(uchar &r, uchar &g, uchar &b) } #endif -/** - Draw XPM image data, with the top-left corner at the given position. - \see fl_draw_pixmap(char* const* data, int x, int y, Fl_Color bg) - */ -int fl_draw_pixmap(const char*const* cdata, int x, int y, Fl_Color bg) { - pixmap_data d; - if (!fl_measure_pixmap(cdata, d.w, d.h)) return 0; +int fl_convert_pixmap(const char*const* cdata, uchar* out, Fl_Color bg) { + int w, h; const uchar*const* data = (const uchar*const*)(cdata+1); int transparent_index = -1; uchar *transparent_c = (uchar *)0; // such that transparent_c[0,1,2] are the RGB of the transparent color + + if (!fl_measure_pixmap(cdata, w, h)) + return 0; + + if ((chars_per_pixel < 1) || (chars_per_pixel > 2)) + return 0; + + uchar (*colors)[4] = new uchar [1<<(chars_per_pixel*8)][4]; + #ifdef WIN32 color_count = 0; used_colors = (uchar *)malloc(abs(ncolors)*3*sizeof(uchar)); #endif - if (ncolors < 0) { // FLTK (non standard) compressed colormap + if (ncolors < 0) { + // FLTK (non standard) compressed colormap ncolors = -ncolors; const uchar *p = *data++; // if first color is ' ' it is transparent (put it later to make // it not be transparent): if (*p == ' ') { - uchar* c = (uchar*)&d.colors[(int)' ']; -#ifdef U64 - *(U64*)c = 0; -# if WORDS_BIGENDIAN - c += 4; -# endif -#endif + uchar* c = colors[(int)' ']; transparent_index = ' '; Fl::get_color(bg, c[0], c[1], c[2]); c[3] = 0; transparent_c = c; @@ -245,13 +151,7 @@ int fl_draw_pixmap(const char*const* cdata, int x, int y, Fl_Color bg) { } // read all the rest of the colors: for (int i=0; i < ncolors; i++) { - uchar* c = (uchar*)&d.colors[*p++]; -#ifdef U64 - *(U64*)c = 0; -# if WORDS_BIGENDIAN - c += 4; -# endif -#endif + uchar* c = colors[*p++]; #ifdef WIN32 used_colors[3*color_count] = *p; used_colors[3*color_count+1] = *(p+1); @@ -261,75 +161,49 @@ int fl_draw_pixmap(const char*const* cdata, int x, int y, Fl_Color bg) { *c++ = *p++; *c++ = *p++; *c++ = *p++; -#ifdef __APPLE_QUARTZ__ *c = 255; -#else - *c = 0; -#endif } - } else { // normal XPM colormap with names - if (chars_per_pixel>1) memset(d.byte1, 0, sizeof(d.byte1)); + } else { + // normal XPM colormap with names for (int i=0; i<ncolors; i++) { const uchar *p = *data++; // the first 1 or 2 characters are the color index: int ind = *p++; uchar* c; - if (chars_per_pixel>1) { -#ifdef U64 - U64* colors = d.byte1[ind]; - if (!colors) colors = d.byte1[ind] = new U64[256]; -#else - U32* colors = d.byte1[ind]; - if (!colors) colors = d.byte1[ind] = new U32[256]; -#endif - c = (uchar*)&colors[*p]; - ind = (ind<<8)|*p++; - } else { - c = (uchar *)&d.colors[ind]; - } + if (chars_per_pixel>1) + ind = (ind<<8)|*p++; + c = colors[ind]; // look for "c word", or last word if none: const uchar *previous_word = p; for (;;) { - while (*p && isspace(*p)) p++; - uchar what = *p++; - while (*p && !isspace(*p)) p++; - while (*p && isspace(*p)) p++; - if (!*p) {p = previous_word; break;} - if (what == 'c') break; - previous_word = p; - while (*p && !isspace(*p)) p++; + while (*p && isspace(*p)) p++; + uchar what = *p++; + while (*p && !isspace(*p)) p++; + while (*p && isspace(*p)) p++; + if (!*p) {p = previous_word; break;} + if (what == 'c') break; + previous_word = p; + while (*p && !isspace(*p)) p++; } -#ifdef U64 - *(U64*)c = 0; -# if WORDS_BIGENDIAN - c += 4; -# endif -#endif -#ifdef __APPLE_QUARTZ__ - c[3] = 255; -#endif int parse = fl_parse_color((const char*)p, c[0], c[1], c[2]); + c[3] = 255; if (parse) { #ifdef WIN32 - used_colors[3*color_count] = c[0]; - used_colors[3*color_count+1] = c[1]; - used_colors[3*color_count+2] = c[2]; - color_count++; + used_colors[3*color_count] = c[0]; + used_colors[3*color_count+1] = c[1]; + used_colors[3*color_count+2] = c[2]; + color_count++; #endif - } - else { + } else { // assume "None" or "#transparent" for any errors - // "bg" should be transparent... - Fl::get_color(bg, c[0], c[1], c[2]); -#ifdef __APPLE_QUARTZ__ + // "bg" should be transparent... + Fl::get_color(bg, c[0], c[1], c[2]); c[3] = 0; -#endif - transparent_index = ind; - transparent_c = c; + transparent_index = ind; + transparent_c = c; } } } - d.data = data; #ifdef WIN32 if (transparent_c) { make_unused_color(transparent_c[0], transparent_c[1], transparent_c[2]); @@ -339,88 +213,89 @@ int fl_draw_pixmap(const char*const* cdata, int x, int y, Fl_Color bg) { make_unused_color(r, g, b); } #endif + + U32 *q = (U32*)out; + for (int Y = 0; Y < h; Y++) { + const uchar* p = data[Y]; + if (chars_per_pixel <= 1) { + for (int X = 0; X < w; X++) + memcpy(q++, colors[*p++], 4); + } else { + for (int X = 0; X < w; X++) { + int ind = (*p++)<<8; + ind |= *p++; + memcpy(q++, colors[ind], 4); + } + } + } + delete [] colors; + return 1; +} + +/** + Draw XPM image data, with the top-left corner at the given position. + \see fl_draw_pixmap(char* const* data, int x, int y, Fl_Color bg) + */ +int fl_draw_pixmap(const char*const* cdata, int x, int y, Fl_Color bg) { + int w, h; + + if (!fl_measure_pixmap(cdata, w, h)) + return 0; + + uchar *buffer = new uchar[w*h*4]; + + if (!fl_convert_pixmap(cdata, buffer, bg)) { + delete buffer; + return 0; + } + + // FIXME: Hack until fl_draw_image() supports alpha properly #ifdef __APPLE_QUARTZ__ if (fl_graphics_driver->class_name() == Fl_Quartz_Graphics_Driver::class_id ) { - bool transparent = (transparent_index>=0); - transparent = true; - U32 *array = new U32[d.w * d.h], *q = array; - for (int Y = 0; Y < d.h; Y++) { - const uchar* p = data[Y]; - if (chars_per_pixel <= 1) { - for (int X = 0; X < d.w; X++) { - *q++ = d.colors[*p++]; - } - } else { - for (int X = 0; X < d.w; X++) { - U32* colors = (U32*)d.byte1[*p++]; - *q++ = colors[*p++]; - } - } - } CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef src = CGDataProviderCreateWithData( 0L, array, d.w * d.h * 4, 0L); - CGImageRef img = CGImageCreate(d.w, d.h, 8, 4*8, 4*d.w, - lut, transparent?kCGImageAlphaLast:kCGImageAlphaNoneSkipLast, - src, 0L, false, kCGRenderingIntentDefault); + CGDataProviderRef src = CGDataProviderCreateWithData( 0L, buffer, w * h * 4, 0L); + CGImageRef img = CGImageCreate(w, h, 8, 4*8, 4*w, + lut, kCGImageAlphaLast, + src, 0L, false, kCGRenderingIntentDefault); CGColorSpaceRelease(lut); CGDataProviderRelease(src); - CGRect rect = { { x, y} , { d.w, d.h } }; - Fl_X::q_begin_image(rect, 0, 0, d.w, d.h); + CGRect rect = { { x, y }, { w, h } }; + Fl_X::q_begin_image(rect, 0, 0, w, h); CGContextDrawImage(fl_gc, rect, img); Fl_X::q_end_image(); CGImageRelease(img); - delete[] array; - } - else { + } else { #endif // __APPLE_QUARTZ__ - // build the mask bitmap used by Fl_Pixmap: - if (fl_mask_bitmap && transparent_index >= 0) { - int W = (d.w+7)/8; - uchar* bitmap = new uchar[W * d.h]; + if (fl_mask_bitmap) { + int W = (w+7)/8; + uchar* bitmap = new uchar[W * h]; *fl_mask_bitmap = bitmap; - for (int Y = 0; Y < d.h; Y++) { - const uchar* p = data[Y]; - if (chars_per_pixel <= 1) { - int dw = d.w; - for (int X = 0; X < W; X++) { - uchar b = (dw-->0 && *p++ != transparent_index); - if (dw-->0 && *p++ != transparent_index) b |= 2; - if (dw-->0 && *p++ != transparent_index) b |= 4; - if (dw-->0 && *p++ != transparent_index) b |= 8; - if (dw-->0 && *p++ != transparent_index) b |= 16; - if (dw-->0 && *p++ != transparent_index) b |= 32; - if (dw-->0 && *p++ != transparent_index) b |= 64; - if (dw-->0 && *p++ != transparent_index) b |= 128; - *bitmap++ = b; - } - } else { - uchar b = 0, bit = 1; - for (int X = 0; X < d.w; X++) { - int ind = *p++; - ind = (ind<<8) | (*p++); - if (ind != transparent_index) b |= bit; - - if (bit < 128) bit <<= 1; - else { - *bitmap++ = b; - b = 0; - bit = 1; - } - } - - if (bit > 1) *bitmap++ = b; + const uchar *p = &buffer[3]; + for (int Y = 0; Y < h; Y++) { + int dw = w; + for (int X = 0; X < W; X++) { + uchar b = 0; + for (int bit = 0x01;bit <= 0x80;bit<<=1) { + if (dw-- < 0) + break; + if (*p > 127) + b |= bit; + p += 4; + } + *bitmap++ = b; } } } - fl_draw_image(chars_per_pixel==1 ? cb1 : cb2, &d, x, y, d.w, d.h, 4); + fl_draw_image(buffer, x, y, w, h, 4); + #ifdef __APPLE_QUARTZ__ } #endif - if (chars_per_pixel > 1) for (int i = 0; i < 256; i++) delete[] d.byte1[i]; + delete buffer; return 1; } diff --git a/common/fltk/src/xutf8/imKStoUCS.c b/common/fltk/src/xutf8/imKStoUCS.c index 0af9a806..802b005e 100644 --- a/common/fltk/src/xutf8/imKStoUCS.c +++ b/common/fltk/src/xutf8/imKStoUCS.c @@ -266,6 +266,12 @@ static unsigned short const keysym_to_unicode_20a0_20ac[] = { 0x20a8, 0x20a9, 0x20aa, 0x20ab, 0x20ac /* 0x20a8-0x20af */ }; +static unsigned short const keysym_to_unicode_fe50_fe60[] = { + 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x0308, /* 0xfe50-0xfe57 */ + 0x030a, 0x030b, 0x030c, 0x0327, 0x0328, 0x1da5, 0x3099, 0x309a, /* 0xfe58-0xfe5f */ + 0x0323 /* 0xfe60-0xfe67 */ +}; + unsigned int KeySymToUcs4(KeySym keysym) { @@ -315,6 +321,8 @@ KeySymToUcs4(KeySym keysym) return keysym_to_unicode_1e9f_1eff[keysym - 0x1e9f]; else if (keysym > 0x209f && keysym < 0x20ad) return keysym_to_unicode_20a0_20ac[keysym - 0x20a0]; + else if (keysym > 0xfe4f && keysym < 0xfe61) + return keysym_to_unicode_fe50_fe60[keysym - 0xfe50]; else return 0; } |