1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/motif/cursor.cpp
3 // Purpose:     wxCursor class
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     17/09/98
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 #ifndef WX_PRECOMP
15     #include "wx/list.h"
16 #endif
17 
18 #include "wx/cursor.h"
19 
20 #ifndef WX_PRECOMP
21     #include "wx/app.h"
22     #include "wx/utils.h"
23     #include "wx/window.h"
24     #include "wx/image.h"
25     #include "wx/log.h"
26 #endif
27 
28 #ifdef __VMS__
29 #pragma message disable nosimpint
30 #endif
31 #include <Xm/Xm.h>
32 #include <X11/cursorfont.h>
33 #ifdef __VMS__
34 #pragma message enable nosimpint
35 #endif
36 
37 #include "wx/motif/private.h"
38 
39 // Cursor for one display, so we can choose the correct one for
40 // the current display.
41 class wxXCursor
42 {
43 public:
44     WXDisplay*  m_display;
45     WXCursor    m_cursor;
46 };
47 
48 WX_DECLARE_LIST(wxXCursor, wxXCursorList);
49 #include "wx/listimpl.cpp"
50 WX_DEFINE_LIST(wxXCursorList)
51 
52 class WXDLLEXPORT wxCursorRefData: public wxGDIRefData
53 {
54 public:
55     wxCursorRefData();
56     virtual ~wxCursorRefData();
57 
58     wxXCursorList m_cursors;  // wxXCursor objects, one per display
59     wxStockCursor m_cursorId; // wxWidgets standard cursor id
60 
61 private:
62     // There is no way to copy m_cursor so we can't implement a copy ctor
63     // properly.
64     wxDECLARE_NO_COPY_CLASS(wxCursorRefData);
65 
66     friend class wxCursor;
67 };
68 
69 #define M_CURSORDATA ((wxCursorRefData *)m_refData)
70 #define M_CURSORHANDLERDATA ((wxCursorRefData *)bitmap->m_refData)
71 
IMPLEMENT_DYNAMIC_CLASS(wxCursor,wxObject)72 IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxObject)
73 
74 wxCursorRefData::wxCursorRefData()
75 {
76     m_cursorId = wxCURSOR_NONE;
77 }
78 
~wxCursorRefData()79 wxCursorRefData::~wxCursorRefData()
80 {
81     wxXCursorList::compatibility_iterator node = m_cursors.GetFirst();
82     while (node)
83     {
84         wxXCursor* c = node->GetData();
85         XFreeCursor((Display*) c->m_display, (Cursor) c->m_cursor);
86         delete c;
87         node = node->GetNext();
88     }
89 }
90 
wxCursor()91 wxCursor::wxCursor()
92 {
93 }
94 
95 #if wxUSE_IMAGE
wxCursor(const wxImage & image)96 wxCursor::wxCursor(const wxImage & image)
97 {
98     unsigned char * rgbBits = image.GetData();
99     int w = image.GetWidth() ;
100     int h = image.GetHeight();
101     bool bHasMask = image.HasMask();
102     int imagebitcount = (w*h)/8;
103 
104     unsigned char * bits = new unsigned char [imagebitcount];
105     unsigned char * maskBits = new unsigned char [imagebitcount];
106 
107     int i, j, i8;
108     unsigned char c, cMask;
109     for (i=0; i<imagebitcount; i++)
110     {
111         bits[i] = 0xff;
112         i8 = i * 8;
113 
114         cMask = 0xfe; // 11111110
115         for (j=0; j<8; j++)
116         {
117             // possible overflow if we do the summation first ?
118             c = (unsigned char)(rgbBits[(i8+j)*3]/3 + rgbBits[(i8+j)*3+1]/3 + rgbBits[(i8+j)*3+2]/3);
119             // if average value is > mid grey
120             if (c>127)
121                 bits[i] = bits[i] & cMask;
122             cMask = (unsigned char)((cMask << 1) | 1);
123         }
124     }
125 
126     if (bHasMask)
127     {
128         unsigned char
129             r = image.GetMaskRed(),
130             g = image.GetMaskGreen(),
131             b = image.GetMaskBlue();
132 
133         for (i=0; i<imagebitcount; i++)
134         {
135             maskBits[i] = 0x0;
136             i8 = i * 8;
137 
138             cMask = 0x1;
139             for (j=0; j<8; j++)
140             {
141                 if (rgbBits[(i8+j)*3] != r || rgbBits[(i8+j)*3+1] != g || rgbBits[(i8+j)*3+2] != b)
142                     maskBits[i] = maskBits[i] | cMask;
143                 cMask = (unsigned char)(cMask << 1);
144             }
145         }
146     }
147     else // no mask
148     {
149         for (i=0; i<imagebitcount; i++)
150             maskBits[i] = 0xFF;
151     }
152 
153     int hotSpotX;
154     int hotSpotY;
155 
156     if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X))
157         hotSpotX = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X);
158     else
159         hotSpotX = 0;
160 
161     if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y))
162         hotSpotY = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y);
163     else
164         hotSpotY = 0;
165 
166     if (hotSpotX < 0 || hotSpotX >= w)
167         hotSpotX = 0;
168     if (hotSpotY < 0 || hotSpotY >= h)
169         hotSpotY = 0;
170 
171     Create( (const char*)bits, w, h, hotSpotX, hotSpotY,
172             (const char*)maskBits );
173 
174     delete[] bits;
175     delete[] maskBits;
176 }
177 #endif
178 
Create(const char bits[],int width,int height,int hotSpotX,int hotSpotY,const char maskBits[])179 void wxCursor::Create(const char bits[], int width, int height,
180                       int hotSpotX, int hotSpotY, const char maskBits[])
181 {
182     if( !m_refData )
183         m_refData = new wxCursorRefData;
184 
185     Display *dpy = (Display*) wxGetDisplay();
186     int screen_num =  DefaultScreen (dpy);
187 
188     Pixmap pixmap = XCreatePixmapFromBitmapData (dpy,
189                                           RootWindow (dpy, screen_num),
190                                           (char*) bits, width, height,
191                                           1 , 0 , 1);
192 
193     Pixmap mask_pixmap = None;
194     if (maskBits != NULL)
195     {
196         mask_pixmap = XCreatePixmapFromBitmapData (dpy,
197                                           RootWindow (dpy, screen_num),
198                                           (char*) maskBits, width, height,
199                                           1 , 0 , 1);
200     }
201 
202     Create( (WXPixmap)pixmap, (WXPixmap)mask_pixmap, hotSpotX, hotSpotY );
203 
204     XFreePixmap( dpy, pixmap );
205     if (mask_pixmap != None)
206     {
207         XFreePixmap( dpy, mask_pixmap );
208     }
209 }
210 
Create(WXPixmap pixmap,WXPixmap mask_pixmap,int hotSpotX,int hotSpotY)211 void wxCursor::Create(WXPixmap pixmap, WXPixmap mask_pixmap,
212                       int hotSpotX, int hotSpotY)
213 {
214     if( !m_refData )
215         m_refData = new wxCursorRefData;
216 
217     Display *dpy = (Display*) wxGetDisplay();
218     int screen_num =  DefaultScreen (dpy);
219 
220     XColor foreground_color;
221     XColor background_color;
222     foreground_color.pixel = BlackPixel(dpy, screen_num);
223     background_color.pixel = WhitePixel(dpy, screen_num);
224     Colormap cmap = (Colormap) wxTheApp->GetMainColormap((WXDisplay*) dpy);
225     XQueryColor(dpy, cmap, &foreground_color);
226     XQueryColor(dpy, cmap, &background_color);
227 
228     Cursor cursor = XCreatePixmapCursor (dpy,
229                                   (Pixmap)pixmap,
230                                   (Pixmap)mask_pixmap,
231                                   &foreground_color,
232                                   &background_color,
233                                   hotSpotX ,
234                                   hotSpotY);
235 
236     if (cursor)
237     {
238         wxXCursor *c = new wxXCursor;
239 
240         c->m_cursor = (WXCursor) cursor;
241         c->m_display = (WXDisplay*) dpy;
242         M_CURSORDATA->m_cursors.Append(c);
243     }
244 }
245 
wxCursor(const char bits[],int width,int height,int hotSpotX,int hotSpotY,const char maskBits[],const wxColour * WXUNUSED (fg),const wxColour * WXUNUSED (bg))246 wxCursor::wxCursor(const char bits[], int width, int height,
247                    int hotSpotX, int hotSpotY, const char maskBits[] ,
248                    const wxColour* WXUNUSED(fg), const wxColour* WXUNUSED(bg) )
249 {
250     Create(bits, width, height, hotSpotX, hotSpotY, maskBits);
251 }
252 
wxCursor(const wxString & name,wxBitmapType type,int hotSpotX,int hotSpotY)253 wxCursor::wxCursor(const wxString& name, wxBitmapType type,
254                    int hotSpotX, int hotSpotY)
255 {
256     // Must be an XBM file
257     if (type != wxBITMAP_TYPE_XBM) {
258         wxLogError("Invalid cursor bitmap type '%d'", type);
259         return;
260     }
261 
262     m_refData = new wxCursorRefData;
263 
264     int hotX = -1, hotY = -1;
265     unsigned int w, h;
266     Pixmap pixmap = None, mask_pixmap = None;
267 
268     Display *dpy = (Display*) wxGetDisplay();
269     int screen_num =  DefaultScreen (dpy);
270 
271     int value = XReadBitmapFile (dpy, RootWindow (dpy, screen_num),
272                                  name.mb_str(),
273                                  &w, &h, &pixmap, &hotX, &hotY);
274 
275     if (value == BitmapSuccess)
276     {
277         // TODO: how do we determine whether hotX, hotY were read correctly?
278         if (hotX < 0 || hotY < 0)
279         {
280             hotX = hotSpotX;
281             hotY = hotSpotY;
282         }
283         if (hotX < 0 || hotY < 0)
284         {
285             hotX = 0;
286             hotY = 0;
287         }
288 
289         Create( (WXPixmap)pixmap, (WXPixmap)mask_pixmap, hotX, hotY );
290 
291         XFreePixmap( dpy, pixmap );
292     }
293 }
294 
295 // Cursors by stock number
InitFromStock(wxStockCursor id)296 void wxCursor::InitFromStock(wxStockCursor id)
297 {
298     m_refData = new wxCursorRefData;
299     M_CURSORDATA->m_cursorId = id;
300 }
301 
~wxCursor()302 wxCursor::~wxCursor()
303 {
304 }
305 
CreateGDIRefData() const306 wxGDIRefData *wxCursor::CreateGDIRefData() const
307 {
308     return new wxCursorRefData;
309 }
310 
311 wxGDIRefData *
CloneGDIRefData(const wxGDIRefData * WXUNUSED (data)) const312 wxCursor::CloneGDIRefData(const wxGDIRefData * WXUNUSED(data)) const
313 {
314     wxFAIL_MSG( wxS("Cloning cursors is not implemented in wxMotif.") );
315 
316     return new wxCursorRefData;
317 }
318 
319 // Motif-specific: create/get a cursor for the current display
GetXCursor(WXDisplay * display) const320 WXCursor wxCursor::GetXCursor(WXDisplay* display) const
321 {
322     if (!M_CURSORDATA)
323         return (WXCursor) 0;
324     wxXCursorList::compatibility_iterator node = M_CURSORDATA->m_cursors.GetFirst();
325     while (node)
326     {
327         wxXCursor* c = node->GetData();
328         if (c->m_display == display)
329             return c->m_cursor;
330         node = node->GetNext();
331     }
332 
333     // No cursor for this display, so let's see if we're an id-type cursor.
334 
335     if (M_CURSORDATA->m_cursorId != wxCURSOR_NONE)
336     {
337         WXCursor cursor = MakeCursor(display, M_CURSORDATA->m_cursorId);
338         if (cursor)
339         {
340             wxXCursor* c = new wxXCursor;
341             c->m_cursor = cursor;
342             c->m_display = display;
343             M_CURSORDATA->m_cursors.Append(c);
344             return cursor;
345         }
346         else
347             return (WXCursor) 0;
348     }
349 
350     // Not an id-type cursor, so we don't know how to create it.
351     return (WXCursor) 0;
352 }
353 
354 // Make a cursor from standard id
MakeCursor(WXDisplay * display,wxStockCursor id) const355 WXCursor wxCursor::MakeCursor(WXDisplay* display, wxStockCursor id) const
356 {
357     Display* dpy = (Display*) display;
358     Cursor cursor = (Cursor) 0;
359     int x_cur = -1;
360 
361     switch (id)
362     {
363     case wxCURSOR_CHAR:             return (WXCursor)cursor;
364 
365     case wxCURSOR_WAIT:             x_cur = XC_watch; break;
366     case wxCURSOR_CROSS:            x_cur = XC_crosshair; break;
367     case wxCURSOR_HAND:             x_cur = XC_hand1; break;
368     case wxCURSOR_BULLSEYE:         x_cur = XC_target; break;
369     case wxCURSOR_PENCIL:           x_cur = XC_pencil; break;
370     case wxCURSOR_MAGNIFIER:        x_cur = XC_sizing; break;
371     case wxCURSOR_IBEAM:            x_cur = XC_xterm; break;
372     case wxCURSOR_NO_ENTRY:         x_cur = XC_pirate; break;
373     case wxCURSOR_LEFT_BUTTON:      x_cur = XC_leftbutton; break;
374     case wxCURSOR_RIGHT_BUTTON:     x_cur = XC_rightbutton; break;
375     case wxCURSOR_MIDDLE_BUTTON:    x_cur =  XC_middlebutton; break;
376     case wxCURSOR_QUESTION_ARROW:   x_cur = XC_question_arrow; break;
377     case wxCURSOR_SIZING:           x_cur = XC_sizing; break;
378     case wxCURSOR_WATCH:            x_cur = XC_watch; break;
379     case wxCURSOR_SPRAYCAN:         x_cur = XC_spraycan; break;
380     case wxCURSOR_PAINT_BRUSH:      x_cur = XC_spraycan; break;
381     case wxCURSOR_SIZENWSE:
382     case wxCURSOR_SIZENESW:         x_cur = XC_crosshair; break;
383     case wxCURSOR_SIZEWE:           x_cur = XC_sb_h_double_arrow; break;
384     case wxCURSOR_SIZENS:           x_cur = XC_sb_v_double_arrow; break;
385     case wxCURSOR_POINT_LEFT:       x_cur = XC_sb_left_arrow; break;
386     case wxCURSOR_POINT_RIGHT:      x_cur = XC_sb_right_arrow; break;
387         // (JD Huggins) added more stock cursors for X
388         // X-only cursors BEGIN
389     case wxCURSOR_CROSS_REVERSE:    x_cur = XC_cross_reverse; break;
390     case wxCURSOR_DOUBLE_ARROW:     x_cur = XC_double_arrow; break;
391     case wxCURSOR_BASED_ARROW_UP:   x_cur = XC_based_arrow_up; break;
392     case wxCURSOR_BASED_ARROW_DOWN: x_cur = XC_based_arrow_down; break;
393     case wxCURSOR_BLANK:
394     {
395         GC gc;
396         XGCValues gcv;
397         Pixmap empty_pixmap;
398         XColor blank_color;
399 
400         empty_pixmap =
401             XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
402                            16, 16, 1);
403         gcv.function = GXxor;
404         gc = XCreateGC (dpy,
405                         empty_pixmap,
406                         GCFunction,
407                         &gcv);
408         XCopyArea (dpy,
409                    empty_pixmap,
410                    empty_pixmap,
411                    gc,
412                    0, 0,
413                    16, 16,
414                    0, 0);
415         XFreeGC (dpy, gc);
416         cursor = XCreatePixmapCursor (dpy,
417                                       empty_pixmap,
418                                       empty_pixmap,
419                                       &blank_color,
420                                       &blank_color,
421                                       8, 8);
422 
423         break;
424     }
425     case wxCURSOR_ARROW:
426     default:       x_cur =  XC_top_left_arrow; break;
427     }
428 
429     if( x_cur == -1 )
430         return (WXCursor)cursor;
431 
432     cursor = XCreateFontCursor (dpy, x_cur);
433     return (WXCursor) cursor;
434 }
435 
436 // Global cursor setting
wxSetCursor(const wxCursor & WXUNUSED (cursor))437 void wxSetCursor(const wxCursor& WXUNUSED(cursor))
438 {
439   // Nothing to do for Motif (no global cursor)
440 }
441 
442 
443 // ----------------------------------------------------------------------------
444 // busy cursor stuff
445 // ----------------------------------------------------------------------------
446 
447 static int wxBusyCursorCount = 0;
448 
449 // Helper function
450 static void
wxXSetBusyCursor(wxWindow * win,const wxCursor * cursor)451 wxXSetBusyCursor (wxWindow * win, const wxCursor * cursor)
452 {
453     Display *display = (Display*) win->GetXDisplay();
454 
455     Window xwin = (Window) win->GetXWindow();
456     if (!xwin)
457        return;
458 
459     XSetWindowAttributes attrs;
460 
461     if (cursor)
462     {
463         attrs.cursor = (Cursor) cursor->GetXCursor(display);
464     }
465     else
466     {
467         // Restore old cursor
468         if (win->GetCursor().IsOk())
469             attrs.cursor = (Cursor) win->GetCursor().GetXCursor(display);
470         else
471             attrs.cursor = None;
472     }
473     if (xwin)
474         XChangeWindowAttributes (display, xwin, CWCursor, &attrs);
475 
476     XFlush (display);
477 
478     for(wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst (); node;
479         node = node->GetNext())
480     {
481         wxWindow *child = node->GetData ();
482         wxXSetBusyCursor (child, cursor);
483     }
484 }
485 
486 // Set the cursor to the busy cursor for all windows
wxBeginBusyCursor(const wxCursor * cursor)487 void wxBeginBusyCursor(const wxCursor *cursor)
488 {
489     wxBusyCursorCount++;
490     if (wxBusyCursorCount == 1)
491     {
492         for(wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst (); node;
493             node = node->GetNext())
494         {
495             wxWindow *win = node->GetData ();
496             wxXSetBusyCursor (win, cursor);
497         }
498     }
499 }
500 
501 // Restore cursor to normal
wxEndBusyCursor()502 void wxEndBusyCursor()
503 {
504     if (wxBusyCursorCount == 0)
505         return;
506 
507     wxBusyCursorCount--;
508     if (wxBusyCursorCount == 0)
509     {
510         for(wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst (); node;
511             node = node->GetNext())
512         {
513             wxWindow *win = node->GetData ();
514             wxXSetBusyCursor (win, NULL);
515         }
516     }
517 }
518 
519 // true if we're between the above two calls
wxIsBusy()520 bool wxIsBusy()
521 {
522     return (wxBusyCursorCount > 0);
523 }
524