1 /********************************************************************************
2 *                                                                               *
3 *                         C u r s o r - O b j e c t                             *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1997,2005 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.55.2.1 2005/02/09 06:30:24 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 FXbool 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 FXbool 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 FXbool 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             dst[0]=src[2];      // B
204             dst[1]=src[1];      // G
205             dst[2]=src[0];      // R
206             dst[3]=src[3];      // A
207             dst+=4;
208             src+=4;
209             }
210           while(src<end);
211           xid=XcursorImageLoadCursor(DISPLAY(getApp()),image);
212           XcursorImageDestroy(image);
213           }
214 
215         // No support for color cursor or simple black/white cursor
216         else{
217 #endif
218           FXuchar shapebits[128],maskbits[128]; XColor color[2]; Pixmap srcpix,mskpix;
219           register FXint srcoffset,dstoffset,dstbytes,i,j;
220           FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
221           color[0].pixel=BlackPixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
222           color[1].pixel=WhitePixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
223           color[0].flags=DoRed|DoGreen|DoBlue;
224           color[1].flags=DoRed|DoGreen|DoBlue;
225           XQueryColors(DISPLAY(getApp()),DefaultColormap(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp()))),color,2);
226           memset(shapebits,0,sizeof(shapebits));
227           memset(maskbits,0,sizeof(maskbits));
228           dstbytes=(width+7)/8;
229           srcoffset=dstoffset=0;
230           for(j=0; j<height; j++){
231             for(i=0; i<width; i++){
232               if(((FXuchar*)(data+srcoffset+i))[3]>=128){
233                 maskbits[dstoffset+(i>>3)]|=(1<<(i&7));
234                 if(DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])) shapebits[dstoffset+(i>>3)]|=(1<<(i&7));
235                 }
236               }
237             srcoffset+=width;
238             dstoffset+=dstbytes;
239             }
240           srcpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)shapebits,width,height);
241           if(!srcpix){ throw FXImageException("unable to create cursor"); }
242           mskpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)maskbits,width,height);
243           if(!mskpix){ throw FXImageException("unable to create cursor"); }
244           xid=XCreatePixmapCursor(DISPLAY(getApp()),srcpix,mskpix,&color[0],&color[1],hotx,hoty);
245           XFreePixmap(DISPLAY(getApp()),srcpix);
246           XFreePixmap(DISPLAY(getApp()),mskpix);
247 #ifdef HAVE_XCURSOR_H
248           }
249 #endif
250         }
251 
252 #else   // WIN32
253 
254       // Mapping to standard WIN32 cursors
255       const LPCTSTR stock[]={IDC_ARROW,IDC_ARROW,IDC_ARROW,IDC_IBEAM,IDC_WAIT,IDC_CROSS,IDC_SIZENS,IDC_SIZEWE,IDC_SIZEALL};
256 
257       // Building stock cursor
258       if(options&CURSOR_MASK){
259         FXTRACE((100,"%s::create: stock cursor\n",getClassName()));
260         xid=LoadCursor(NULL,stock[options&CURSOR_MASK]);
261         }
262 
263       // Building custom cursor
264       else{
265 
266         // Should have data
267         if(!data){ fxerror("%s::create: cursor needs pixel data.\n",getClassName()); }
268 
269         // Let's hope it's the correct size!
270         if(width>32 || height>32){ fxerror("%s::create: cursor exceeds maximum size of 32x32 pixels\n",getClassName()); }
271 
272         FXASSERT(GetSystemMetrics(SM_CXCURSOR)==32);
273         FXASSERT(GetSystemMetrics(SM_CYCURSOR)==32);
274 
275         // We have support for color cursors and its a color cursor
276         if(isColor() && supportsColorCursors()){
277           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};
278           HBITMAP img,mask;
279           ICONINFO ii;
280           FXTRACE((100,"%s::create: custom color %dx%d cursor\n",getClassName(),width,height));
281 
282           // Make a DIB
283           void *imgdata=0;
284           HDC hdc=GetDC(NULL);
285           img=CreateDIBSection(hdc,(BITMAPINFO*)&bi,DIB_RGB_COLORS,&imgdata,NULL,0);
286           ReleaseDC(NULL,hdc);
287           if(!img){ throw FXImageException("unable to create cursor"); }
288 
289           // Fill in data
290           FXuint *imgptr=(FXuint*)imgdata;
291           FXColor *srcimgptr=data;
292           for(int y=0; y<height; y++){
293             for(int x=0; x<width; x++){
294               FXColor col=*srcimgptr++;
295               *imgptr++=(FXALPHAVAL(col)<<24)|(FXREDVAL(col)<<16)|(FXGREENVAL(col)<<8)|(FXBLUEVAL(col));
296               }
297             for(int fill=width; fill<32; fill++){
298               *imgptr++=0;
299               }
300             }
301           if(height<32) memset(imgptr,0,(32-height)*32);
302 
303           // Strawman mask bitmap
304           mask=CreateBitmap(32,32,1,1,NULL);
305           if(!mask){ throw FXImageException("unable to create cursor"); }
306 
307           // Create cursor
308           ii.fIcon=FALSE;
309           ii.xHotspot=hotx;
310           ii.yHotspot=hoty;
311           ii.hbmMask=mask;
312           ii.hbmColor=img;
313           xid=CreateIconIndirect(&ii);
314 
315           // No longer needed
316           DeleteObject(mask);
317           DeleteObject(img);
318           }
319 
320         // No support for color cursor or simple black/white cursor
321         else{
322           FXint i,j,srcbytes,srcoffset,dstoffset; FXuchar tmpxor[128],tmpand[128];
323           FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
324           srcbytes=(width+7)/8;
325           srcoffset=dstoffset=0;
326           memset(tmpand,0xff,sizeof(tmpand));
327           memset(tmpxor,0,sizeof(tmpxor));
328           for(j=0; j<height; j++){
329             for(i=0; i<width; i++){
330               if(((FXuchar*)(data+srcoffset+i))[3]>=128){
331                 tmpand[dstoffset+(i>>3)]&=~(128>>(i&7));
332                 if(!DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])){
333                   tmpxor[dstoffset+(i>>3)]|=(128>>(i&7));
334                   }
335                 }
336               }
337             srcoffset+=width;
338             dstoffset+=4;
339             }
340           xid=CreateCursor((HINSTANCE)(getApp()->display),hotx,hoty,32,32,tmpand,tmpxor);
341           }
342         }
343 
344 #endif
345 
346       // Were we successful?
347       if(!xid){ throw FXImageException("unable to create cursor"); }
348 
349       // Release pixel buffer
350       if(!(options&CURSOR_KEEP)) release();
351       }
352     }
353   }
354 
355 
356 // Detach cursor
detach()357 void FXCursor::detach(){
358   if(xid){
359     FXTRACE((100,"%s::detach %p\n",getClassName(),this));
360     xid=0;
361     }
362   }
363 
364 
365 // Release pixels buffer if it was owned
release()366 void FXCursor::release(){
367   if(options&CURSOR_OWNED){
368     options&=~CURSOR_OWNED;
369     FXFREE(&data);
370     }
371   data=NULL;
372   }
373 
374 
375 // Destroy cursor
destroy()376 void FXCursor::destroy(){
377   if(xid){
378     if(getApp()->isInitialized()){
379       FXTRACE((100,"%s::destroy %p\n",getClassName(),this));
380 #ifndef WIN32
381 
382       // Delete cursor
383       XFreeCursor(DISPLAY(getApp()),xid);
384 #else
385 
386       // Delete cursor
387       DestroyCursor((HCURSOR)xid);
388 
389 #endif
390       }
391     xid=0;
392     }
393   }
394 
395 
396 // Save pixel data only
savePixels(FXStream & store) const397 FXbool FXCursor::savePixels(FXStream& store) const {
398   FXuint size=width*height;
399   store.save(data,size);
400   return TRUE;
401   }
402 
403 
404 // Load pixel data only
loadPixels(FXStream & store)405 FXbool FXCursor::loadPixels(FXStream& store){
406   FXuint size=width*height;
407   if(options&CURSOR_OWNED){FXFREE(&data);}
408   if(!FXMALLOC(&data,FXColor,size)) return FALSE;
409   store.load(data,size);
410   options|=CURSOR_OWNED;
411   return TRUE;
412   }
413 
414 
415 // Save cursor to stream
save(FXStream & store) const416 void FXCursor::save(FXStream& store) const {
417   FXuchar haspixels=(data!=NULL);
418   FXId::save(store);
419   store << width << height << hotx << hoty;
420   store << options;
421   store << haspixels;
422   if(haspixels) savePixels(store);
423   }
424 
425 
426 // Load cursor from stream
load(FXStream & store)427 void FXCursor::load(FXStream& store){
428   FXuchar haspixels;
429   FXId::load(store);
430   store >> width >> height >> hotx >> hoty;
431   store >> options;
432   store >> haspixels;
433   if(haspixels) loadPixels(store);
434   }
435 
436 
437 // Clean up
~FXCursor()438 FXCursor::~FXCursor(){
439   FXTRACE((100,"FXCursor::~FXCursor %p\n",this));
440   destroy();
441   if(options&CURSOR_OWNED){FXFREE(&data);}
442   data=(FXColor *)-1L;
443   }
444 
445 }
446