1 /********************************************************************************
2 *                                                                               *
3 *                         C u r s o r - O b j e c t                             *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1997,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXCursor.cpp,v 1.62.2.1 2006/06/09 00:50:16 fox Exp $                        *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXThread.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXSettings.h"
35 #include "FXRegistry.h"
36 #include "FXApp.h"
37 #include "FXId.h"
38 #include "FXVisual.h"
39 #include "FXCursor.h"
40 #include "FXException.h"
41 
42 
43 /*
44   Notes:
45   - Cursor size should be less than or equal to 32x32; limitation in Windows!
46   - Need standard glyph for "invisible" cursor.
47   - Keep hotx and hoty INSIDE the cursor glyph!!
48   - Thanks Niall Douglas <s_sourceforge@nedprod.com> for the changes for
49     alpha-blended cursors.
50 */
51 
52 #define DISPLAY(app)     ((Display*)((app)->display))
53 #define DARKCOLOR(r,g,b) (((r)+(g)+(b))<382)
54 #define CURSOR_MASK      (255)
55 
56 
57 using namespace FX;
58 
59 /*******************************************************************************/
60 
61 namespace FX {
62 
63 extern bool fxloadXBM(FXColor*& data,const FXuchar *pixels,const FXuchar *mask,FXint width,FXint height);
64 
65 
66 // Standard colors
67 const FXColor white=FXRGBA(255,255,255,255);
68 const FXColor black=FXRGBA(0,0,0,255);
69 
70 
71 // Object implementation
72 FXIMPLEMENT(FXCursor,FXId,NULL,0)
73 
74 
75 // Deserialization
FXCursor()76 FXCursor::FXCursor(){
77   data=NULL;
78   width=0;
79   height=0;
80   hotx=0;
81   hoty=0;
82   options=CURSOR_ARROW;
83   }
84 
85 
86 // Make stock cursor
FXCursor(FXApp * a,FXStockCursor curid)87 FXCursor::FXCursor(FXApp* a,FXStockCursor curid):FXId(a){
88   FXTRACE((100,"FXCursor::FXCursor %p\n",this));
89   data=NULL;
90   width=0;
91   height=0;
92   hotx=0;
93   hoty=0;
94   options=curid;
95   }
96 
97 
98 // Make cursor from source and mask
FXCursor(FXApp * a,const FXuchar * src,const FXuchar * msk,FXint w,FXint h,FXint hx,FXint hy)99 FXCursor::FXCursor(FXApp* a,const FXuchar* src,const FXuchar* msk,FXint w,FXint h,FXint hx,FXint hy):FXId(a){
100   FXTRACE((100,"FXCursor::FXCursor %p\n",this));
101   fxloadXBM(data,src,msk,w,h);
102   width=w;
103   height=h;
104   hotx=FXCLAMP(0,hx,width-1);
105   hoty=FXCLAMP(0,hy,height-1);
106   options=CURSOR_OWNED;
107   }
108 
109 
110 // Make cursor from FXColor pixels
FXCursor(FXApp * a,const FXColor * pix,FXint w,FXint h,FXint hx,FXint hy)111 FXCursor::FXCursor(FXApp* a,const FXColor *pix,FXint w,FXint h,FXint hx,FXint hy):FXId(a){
112   FXTRACE((100,"FXCursor::FXCursor %p\n",this));
113   data=(FXColor*)pix;
114   width=w;
115   height=h;
116   hotx=FXCLAMP(0,hx,width-1);
117   hoty=FXCLAMP(0,hy,height-1);
118   options=0;
119   }
120 
121 
122 // Return TRUE if color cursor
isColor() const123 bool FXCursor::isColor() const {
124   register FXint i;
125   if(data){
126     for(i=width*height-1; 0<=i; i--){
127       if(data[i]!=black && data[i]!=white && FXALPHAVAL(data[i])!=0) return true;
128       }
129     }
130   return false;
131   }
132 
133 
134 #ifdef WIN32
135 
supportsColorCursors()136 static bool supportsColorCursors(){
137 
138   // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
139   // If that fails, try using the OSVERSIONINFO structure.
140 #if defined (__WATCOMC__) || (__DMC__)
141   OSVERSIONINFO osvi={sizeof(OSVERSIONINFO)};
142 #else
143   OSVERSIONINFOEX osvi={sizeof(OSVERSIONINFOEX)};
144 #endif
145   if(!GetVersionEx((OSVERSIONINFO*)&osvi)){
146 
147     // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
148     osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
149     if(!GetVersionEx((OSVERSIONINFO*)&osvi)){
150       return false; // should not happen
151       }
152     }
153   if(osvi.dwPlatformId==VER_PLATFORM_WIN32_NT){
154     if(osvi.dwMajorVersion==5 && osvi.dwMinorVersion>=0 || osvi.dwMajorVersion>5){
155       return true;
156       }
157     }
158 
159   return false;
160   }
161 
162 
163 #endif
164 
165 // Create cursor
create()166 void FXCursor::create(){
167   if(!xid){
168     if(getApp()->isInitialized()){
169       FXTRACE((100,"%s::create %p\n",getClassName(),this));
170 
171 #ifndef WIN32   // X11
172 
173       // Mapping to standard X11 cursors
174       const FXuint stock[]={XC_left_ptr,XC_left_ptr,XC_right_ptr,XC_xterm,XC_watch,XC_crosshair,XC_sb_h_double_arrow,XC_sb_v_double_arrow,XC_fleur};
175 
176       // Building stock cursor
177       if(options&CURSOR_MASK){
178         FXTRACE((100,"%s::create: stock cursor\n",getClassName()));
179         xid=XCreateFontCursor(DISPLAY(getApp()),stock[options&CURSOR_MASK]);
180         }
181 
182       // Building custom cursor
183       else{
184 
185         // Should have data
186         if(!data){ fxerror("%s::create: cursor needs pixel data.\n",getClassName()); }
187 
188         // Let's hope it's the correct size!
189         if(width>32 || height>32){ fxerror("%s::create: cursor exceeds maximum size of 32x32 pixels\n",getClassName()); }
190 
191         // We have support for color cursors and its a color cursor
192 #ifdef HAVE_XCURSOR_H
193         if(isColor() && XcursorSupportsARGB(DISPLAY(getApp()))){
194           register FXuchar *src,*dst,*end; XcursorImage *image;
195           FXTRACE((100,"%s::create: custom color %dx%d cursor\n",getClassName(),width,height));
196           image=XcursorImageCreate(width,height);
197           image->xhot=hotx;
198           image->yhot=hoty;
199           dst=(FXuchar*)image->pixels;
200           src=(FXuchar*)data;
201           end=src+width*height*4;
202           do{
203 #ifndef __APPLE__
204             dst[0]=src[2];      // B
205             dst[1]=src[1];      // G
206             dst[2]=src[0];      // R
207             dst[3]=src[3];      // A
208 #else
209             // A bug in Apple's X11 implementation has alpha on
210             // the wrong end and BGR wrong way round
211             dst[0]=src[3];      // A
212             dst[3]=src[2];      // B
213             dst[2]=src[1];      // G
214             dst[1]=src[0];      // R
215 #endif
216             dst+=4;
217             src+=4;
218             }
219           while(src<end);
220           xid=XcursorImageLoadCursor(DISPLAY(getApp()),image);
221           XcursorImageDestroy(image);
222           }
223 
224         // No support for color cursor or simple black/white cursor
225         else{
226 #endif
227           FXuchar shapebits[128],maskbits[128]; XColor color[2]; Pixmap srcpix,mskpix;
228           register FXint srcoffset,dstoffset,dstbytes,i,j;
229           FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
230           color[0].pixel=BlackPixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
231           color[1].pixel=WhitePixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
232           color[0].flags=DoRed|DoGreen|DoBlue;
233           color[1].flags=DoRed|DoGreen|DoBlue;
234           XQueryColors(DISPLAY(getApp()),DefaultColormap(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp()))),color,2);
235           memset(shapebits,0,sizeof(shapebits));
236           memset(maskbits,0,sizeof(maskbits));
237           dstbytes=(width+7)/8;
238           srcoffset=dstoffset=0;
239           for(j=0; j<height; j++){
240             for(i=0; i<width; i++){
241               if(((FXuchar*)(data+srcoffset+i))[3]>=128){
242                 maskbits[dstoffset+(i>>3)]|=(1<<(i&7));
243                 if(DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])) shapebits[dstoffset+(i>>3)]|=(1<<(i&7));
244                 }
245               }
246             srcoffset+=width;
247             dstoffset+=dstbytes;
248             }
249           srcpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)shapebits,width,height);
250           if(!srcpix){ throw FXImageException("unable to create cursor"); }
251           mskpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)maskbits,width,height);
252           if(!mskpix){ throw FXImageException("unable to create cursor"); }
253           xid=XCreatePixmapCursor(DISPLAY(getApp()),srcpix,mskpix,&color[0],&color[1],hotx,hoty);
254           XFreePixmap(DISPLAY(getApp()),srcpix);
255           XFreePixmap(DISPLAY(getApp()),mskpix);
256 #ifdef HAVE_XCURSOR_H
257           }
258 #endif
259         }
260 
261 #else   // WIN32
262 
263       // Mapping to standard WIN32 cursors
264       const LPCTSTR stock[]={IDC_ARROW,IDC_ARROW,IDC_ARROW,IDC_IBEAM,IDC_WAIT,IDC_CROSS,IDC_SIZENS,IDC_SIZEWE,IDC_SIZEALL};
265 
266       // Building stock cursor
267       if(options&CURSOR_MASK){
268         FXTRACE((100,"%s::create: stock cursor\n",getClassName()));
269         xid=LoadCursor(NULL,stock[options&CURSOR_MASK]);
270         }
271 
272       // Building custom cursor
273       else{
274 
275         // Should have data
276         if(!data){ fxerror("%s::create: cursor needs pixel data.\n",getClassName()); }
277 
278         // Let's hope it's the correct size!
279         if(width>32 || height>32){ fxerror("%s::create: cursor exceeds maximum size of 32x32 pixels\n",getClassName()); }
280 
281         FXASSERT(GetSystemMetrics(SM_CXCURSOR)==32);
282         FXASSERT(GetSystemMetrics(SM_CYCURSOR)==32);
283 
284         // We have support for color cursors and its a color cursor
285         if(isColor() && supportsColorCursors()){
286           const BITMAPV4HEADER bi={sizeof(BITMAPV4HEADER),32,-32,1,32,BI_BITFIELDS,0,0,0,0,0,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000,0,{{0,0,0},{0,0,0},{0,0,0}},0,0,0};
287           HBITMAP img,mask;
288           ICONINFO ii;
289           FXTRACE((100,"%s::create: custom color %dx%d cursor\n",getClassName(),width,height));
290 
291           // Make a DIB
292           void *imgdata=0;
293           HDC hdc=GetDC(NULL);
294           img=CreateDIBSection(hdc,(BITMAPINFO*)&bi,DIB_RGB_COLORS,&imgdata,NULL,0);
295           ReleaseDC(NULL,hdc);
296           if(!img){ throw FXImageException("unable to create cursor"); }
297 
298           // Fill in data
299           FXuint *imgptr=(FXuint*)imgdata;
300           FXColor *srcimgptr=data;
301           for(int y=0; y<height; y++){
302             for(int x=0; x<width; x++){
303               FXColor col=*srcimgptr++;
304               *imgptr++=(FXALPHAVAL(col)<<24)|(FXREDVAL(col)<<16)|(FXGREENVAL(col)<<8)|(FXBLUEVAL(col));
305               }
306             for(int fill=width; fill<32; fill++){
307               *imgptr++=0;
308               }
309             }
310           if(height<32) memset(imgptr,0,(32-height)*32);
311 
312           // Strawman mask bitmap
313           mask=CreateBitmap(32,32,1,1,NULL);
314           if(!mask){ throw FXImageException("unable to create cursor"); }
315 
316           // Create cursor
317           ii.fIcon=FALSE;
318           ii.xHotspot=hotx;
319           ii.yHotspot=hoty;
320           ii.hbmMask=mask;
321           ii.hbmColor=img;
322           xid=CreateIconIndirect(&ii);
323 
324           // No longer needed
325           DeleteObject(mask);
326           DeleteObject(img);
327           }
328 
329         // No support for color cursor or simple black/white cursor
330         else{
331           FXint i,j,srcoffset,dstoffset; FXuchar tmpxor[128],tmpand[128];
332           FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
333           srcoffset=dstoffset=0;
334           memset(tmpand,0xff,sizeof(tmpand));
335           memset(tmpxor,0,sizeof(tmpxor));
336           for(j=0; j<height; j++){
337             for(i=0; i<width; i++){
338               if(((FXuchar*)(data+srcoffset+i))[3]>=128){
339                 tmpand[dstoffset+(i>>3)]&=~(128>>(i&7));
340                 if(!DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])){
341                   tmpxor[dstoffset+(i>>3)]|=(128>>(i&7));
342                   }
343                 }
344               }
345             srcoffset+=width;
346             dstoffset+=4;
347             }
348           xid=CreateCursor((HINSTANCE)(getApp()->display),hotx,hoty,32,32,tmpand,tmpxor);
349           }
350         }
351 
352 #endif
353 
354       // Were we successful?
355       if(!xid){ throw FXImageException("unable to create cursor"); }
356 
357       // Release pixel buffer
358       if(!(options&CURSOR_KEEP)) release();
359       }
360     }
361   }
362 
363 
364 // Detach cursor
detach()365 void FXCursor::detach(){
366   if(xid){
367     FXTRACE((100,"%s::detach %p\n",getClassName(),this));
368     xid=0;
369     }
370   }
371 
372 
373 // Release pixels buffer if it was owned
release()374 void FXCursor::release(){
375   if(options&CURSOR_OWNED){
376     options&=~CURSOR_OWNED;
377     FXFREE(&data);
378     }
379   data=NULL;
380   }
381 
382 
383 // Destroy cursor
destroy()384 void FXCursor::destroy(){
385   if(xid){
386     if(getApp()->isInitialized()){
387       FXTRACE((100,"%s::destroy %p\n",getClassName(),this));
388 #ifndef WIN32
389 
390       // Delete cursor
391       XFreeCursor(DISPLAY(getApp()),xid);
392 #else
393 
394       // Delete cursor
395       DestroyCursor((HCURSOR)xid);
396 
397 #endif
398       }
399     xid=0;
400     }
401   }
402 
403 
404 // Save pixel data only
savePixels(FXStream & store) const405 bool FXCursor::savePixels(FXStream& store) const {
406   FXuint size=width*height;
407   store.save(data,size);
408   return true;
409   }
410 
411 
412 // Load pixel data only
loadPixels(FXStream & store)413 bool FXCursor::loadPixels(FXStream& store){
414   FXuint size=width*height;
415   if(options&CURSOR_OWNED){FXFREE(&data);}
416   if(!FXMALLOC(&data,FXColor,size)) return false;
417   store.load(data,size);
418   options|=CURSOR_OWNED;
419   return true;
420   }
421 
422 
423 // Save cursor to stream
save(FXStream & store) const424 void FXCursor::save(FXStream& store) const {
425   FXuchar haspixels=(data!=NULL);
426   FXId::save(store);
427   store << width << height << hotx << hoty;
428   store << options;
429   store << haspixels;
430   if(haspixels) savePixels(store);
431   }
432 
433 
434 // Load cursor from stream
load(FXStream & store)435 void FXCursor::load(FXStream& store){
436   FXuchar haspixels;
437   FXId::load(store);
438   store >> width >> height >> hotx >> hoty;
439   store >> options;
440   store >> haspixels;
441   if(haspixels) loadPixels(store);
442   }
443 
444 
445 // Clean up
~FXCursor()446 FXCursor::~FXCursor(){
447   FXTRACE((100,"FXCursor::~FXCursor %p\n",this));
448   destroy();
449   if(options&CURSOR_OWNED){FXFREE(&data);}
450   data=(FXColor *)-1L;
451   }
452 
453 }
454