more horizontal refactoring
[swftools.git] / lib / gfxwindow_unix.c
1 /* gfxwindow.h 
2
3    Simple GUI abstraction- Unix implementation
4
5    Part of the swftools package.
6
7    Copyright (c) 2005 Matthias Kramm <kramm@quiss.org> 
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <pthread.h>
26 #include <memory.h>
27 #include <unistd.h>
28 #include <sys/ipc.h>
29 #include <sys/shm.h>
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <X11/Xresource.h>
33 #include <X11/xpm.h>
34 #include <X11/Xthreads.h>
35 #include <X11/Xatom.h>
36 #include <X11/extensions/XShm.h>
37 #include "gfxwindow.h"
38 #include "types.h"
39
40 #define USE_SHM 1
41 #define USE_PIXMAP 1 // use pixmap instead of ximage for off-screen rendering
42
43 #define initMutex(mutex) {pthread_mutex_init(mutex, 0);}
44 #define destroyMutex(mutex) {pthread_mutex_destroy((mutex));}
45 #define lockMutex(mutex) pthread_mutex_lock(mutex)
46 #define unlockMutex(mutex) pthread_mutex_unlock(mutex)
47
48 typedef struct _queue
49 {
50     char* data;
51     int readpos;
52     int writepos;
53     int number;
54     int size;
55     int nmemb;
56     pthread_mutex_t mutex;
57 } queue_t;
58
59
60 queue_t* queue_init(int size, int nmemb)
61 {
62     queue_t* queue = malloc(sizeof(queue_t));
63     queue->data = (char*)malloc(size * nmemb);
64     queue->size = size;
65     queue->nmemb = nmemb;
66     queue->readpos = 0;
67     queue->writepos = 0;
68     queue->number = 0;
69     initMutex(&queue->mutex);
70     return queue;
71 }
72
73 void queue_destroy(queue_t*queue)
74 {
75     free(queue->data);queue->data = 0;
76     destroyMutex(&queue->mutex);
77     memset(queue, 0, sizeof(queue_t));
78 }
79
80 int queue_put(queue_t*queue, void*data)
81 {
82     int tmp = queue->writepos+1;
83     tmp%=queue->nmemb;
84
85     lockMutex(&queue->mutex);
86     if(tmp==queue->readpos) {
87         unlockMutex(&queue->mutex);
88         return 0;
89     }
90     memcpy(&queue->data[queue->writepos*queue->size], data, queue->size);
91     queue->writepos = tmp;
92     queue->number++;
93     unlockMutex(&queue->mutex);
94     return 1;
95 }
96
97 int queue_get(queue_t*queue, void*data)
98 {
99     lockMutex(&queue->mutex);
100     if(queue->writepos == queue->readpos) {
101         unlockMutex(&queue->mutex);
102         return 0;
103     }
104     memcpy(data, &queue->data[queue->readpos*queue->size], queue->size);
105     queue->readpos++;
106     queue->readpos %= queue->nmemb;
107     queue->number--;
108     unlockMutex(&queue->mutex);
109     return 1;
110 }
111
112 void queue_flush(queue_t*queue)
113 {
114     lockMutex(&queue->mutex);
115     queue->number = 0;
116     queue->readpos = 0;
117     queue->writepos = 0;
118     unlockMutex(&queue->mutex);
119 }
120
121 int queue_num(queue_t*queue)
122 {
123     return queue->number;
124 }
125
126 static int initializexwindows();
127
128 struct _QXWindow;
129 struct X
130 {
131     char isnative24;
132     char isnative16;
133     char isnative8;
134     int bpp;
135     int bypp;
136     Atom atom_wm;
137     Atom atom_wmdelete;
138
139     int rootsizex;
140     int rootsizey;
141
142     Display *d;
143     Window root;
144     int s;
145     Visual*v;
146
147     char running;
148     pthread_t eventhandler;
149     pthread_mutex_t xmutex;
150
151     struct _QXWindow*windowring;
152
153     int stopall;
154     Colormap cmap;
155     U32 white;
156     U32 black;
157 }; 
158 static struct X X;
159
160 static int initialized = 0;
161    
162 void destroyX() {
163     X.stopall=1;
164     if(X.running)
165       pthread_cancel(X.eventhandler);
166     if(X.d)
167       XCloseDisplay(X.d);
168     pthread_mutex_destroy(&X.xmutex);
169 }
170
171 typedef struct _QXWindow {
172     struct _QXWindow*next;
173     struct _QXWindow*prev;
174
175     XWMHints *xwmh;
176     XClassHint *xch;
177
178     GC gc;
179     //XFontStruct *xfs;
180
181     XSetWindowAttributes xswa;
182     XWindowAttributes xwa;
183
184 #ifdef USE_PIXMAP
185     Pixmap offscreen_pixmap;
186 #else
187     XImage* offscreen_ximage;
188 #endif
189
190     U8*currentscr;
191
192     int x,y;
193     int lenx,leny;
194     int bpp;
195     int bypp;
196     Window mywin;
197     XGCValues xgcv;
198     XSizeHints *xsh;
199     
200     pthread_mutex_t wmutex;
201
202     queue_t* queue;
203 } QXWindow;
204
205 static int lastbit(U32 bits)
206 {
207     int t;
208     for(t=0;t<32;t++)
209     {
210         if(bits&1) break;
211         bits>>=1;
212     }
213     return t;
214 }
215
216 static int firstbit(U32 bits)
217 {
218     int t,s=-1;
219     for(t=0;t<32;t++)
220     {
221         if(bits&1) s=t;
222         bits>>=1;
223     }
224     return s;
225 }
226
227 static U32 shiftl(U32 bits,int shift)
228 {
229     if(shift>0) return bits<<shift;
230     else        return bits>>-shift;
231 }
232
233 static U32 getColorFromRGB(U8 r,U8 g,U8 b)
234 {
235     U32 ret=0;
236     ret|=shiftl(r,(firstbit(X.v->red_mask)-7))&X.v->red_mask;
237     ret|=shiftl(g,(firstbit(X.v->green_mask)-7))&X.v->green_mask;
238     ret|=shiftl(b,(firstbit(X.v->blue_mask)-7))&X.v->blue_mask;
239     return ret;
240 }
241
242 static void whatever()
243 {
244             XrmValue xrmvalue;
245             XrmDatabase rdb;
246             return;
247 }
248
249 static void*eventloop(void*);
250 static int debug;
251 static int initializexwindows()
252 {
253     if(initialized)
254         return 1;
255     X.windowring=0;
256     X.d=0;
257     X.running=0;
258     pthread_mutex_init(&X.xmutex, 0);
259
260     //whatever();
261     /* open display, and show some information */
262     X.d=XOpenDisplay(0);//getenv("DISPLAY"));
263     if(!X.d) {return 0;}
264 /*    printf("Xprotocol V%d.%d Vendor:%s Name:%s Defaults:%s",X.d->proto_major_version,
265                                X.d->proto_minor_version,
266                                X.d->vendor,X.d->display_name,X.d->defaults);
267     int t;
268     printf("Formats:\n");
269     for(t=0;t<X.d->nformats;t++) {
270         ScreenFormat*sf=&X.d->pixmap_formats[t];
271         printf("%d:depth:%d, bpp:%d, scanline:%d\n",sf->depth,sf->bits_per_pixel,sf->scanline_pad);
272     }
273     printf("Screens:\n");
274     for(t=0;t<X.d->nscreens;t++) {
275         Screen*s=&X.d->screens[t];
276         printf("%d*%d, bpp:%d\n",s->width,s->height,s->root_depth);
277     }*/
278
279     /* open screen */
280     X.s=DefaultScreen(X.d);
281     if(debug) printf("Display:\n");
282     int xbig=DisplayWidth(X.d, X.s);
283     int ybig=DisplayHeight(X.d, X.s);
284     X.rootsizex=xbig;
285     X.rootsizey=ybig;
286     int bppbig=X.bpp=DisplayPlanes(X.d, X.s);
287     if(X.bpp==24) X.bypp=4; //Can this be 3, also?
288     if(X.bpp==16) X.bypp=2;
289     if(X.bpp==8) X.bypp=1;
290     if(debug) printf("%d*%d:%d/%d/%d\n",xbig,ybig,bppbig, X.bpp, X.bypp);
291     if(debug) printf("%d screens, vendor:%s, Ver:%d.%d(%d), at %s\n",
292     ScreenCount(X.d),
293     ServerVendor(X.d),
294     ProtocolVersion(X.d),
295     ProtocolRevision(X.d),
296     VendorRelease(X.d),
297     DisplayString(X.d));
298
299     /* open visual */
300     X.v=XDefaultVisualOfScreen(DefaultScreenOfDisplay(X.d));
301
302     X.isnative24 = (bppbig==24 && X.v->red_mask==0xff0000 &&
303                       X.v->green_mask==0x00ff00 &&
304                       X.v->blue_mask==0x0000ff);
305     X.isnative16 = (bppbig==16 && X.v->red_mask==0xf800 &&
306                       X.v->green_mask==0x07e0 &&
307                       X.v->blue_mask==0x1f);
308     X.isnative8 = (bppbig==8);
309     if(debug) printf("8bpp:%d 16bpp:%d 24(32)bpp:%d\n",X.isnative8,X.isnative16,X.isnative24);
310
311     X.root=DefaultRootWindow(X.d);
312     if(!X.root) {return 0;}
313
314     X.atom_wm = XInternAtom( X.d, "WM_PROTOCOLS", False );
315     X.atom_wmdelete = XInternAtom( X.d, "WM_DELETE_WINDOW", False );
316
317     X.cmap=DefaultColormap(X.d,X.s);
318     X.white=WhitePixel(X.d,X.s);
319     X.black=BlackPixel(X.d,X.s);
320
321     /* start event handler thread */
322     X.running = 1;
323     pthread_create(&X.eventhandler,0,eventloop,0);
324
325     X.stopall=0;
326     
327     initialized=1;
328     return 1;
329 }
330
331 //needed to set the border attribute
332 #define MWM_HINTS_FUNCTIONS     (1L << 0)
333 #define MWM_HINTS_DECORATIONS   (1L << 1)
334 #define MWM_HINTS_INPUT_MODE    (1L << 2)
335 #define MWM_HINTS_STATUS        (1L << 3)
336
337 #define MWM_FUNC_ALL            (1L << 0)
338 #define MWM_FUNC_RESIZE         (1L << 1)
339 #define MWM_FUNC_MOVE           (1L << 2)
340 #define MWM_FUNC_MINIMIZE       (1L << 3)
341 #define MWM_FUNC_MAXIMIZE       (1L << 4)
342 #define MWM_FUNC_CLOSE          (1L << 5)
343
344 #define MWM_DECOR_ALL           (1L << 0)
345 #define MWM_DECOR_BORDER        (1L << 1)
346 #define MWM_DECOR_RESIZEH       (1L << 2)
347 #define MWM_DECOR_TITLE         (1L << 3)
348 #define MWM_DECOR_MENU          (1L << 4)
349 #define MWM_DECOR_MINIMIZE      (1L << 5)
350 #define MWM_DECOR_MAXIMIZE      (1L << 6)
351
352 #define MWM_INPUT_MODELESS 0
353 #define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
354 #define MWM_INPUT_SYSTEM_MODAL 2
355 #define MWM_INPUT_FULL_APPLICATION_MODAL 3
356 #define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
357
358 typedef struct _mwmhints {
359   unsigned int flags;
360   unsigned int functions;
361   unsigned int decorations;
362   int input_mode;
363   unsigned int status;
364 } MWMHints;
365
366 static QXWindow* openwindow(int posx,int posy,int w,int h,char*winName)
367 {
368     int noborder = 0;
369     XTextProperty iname;
370     XTextProperty wname;
371
372     pthread_mutex_lock(&X.xmutex);
373
374     if(!initialized) {
375         fprintf(stderr, "Error: XWindows not yet initialized!\n");
376         pthread_mutex_unlock(&X.xmutex);
377         return 0;
378     }
379
380     QXWindow*qx= malloc(sizeof(QXWindow));
381     if(!X.windowring) {
382         qx->next=qx;
383         qx->prev=qx;
384         X.windowring=qx;
385     } else {
386         qx->next=X.windowring;
387         qx->prev=X.windowring->prev;
388         qx->prev->next=qx;
389         X.windowring->prev=qx;
390         X.windowring=qx;
391     }
392
393     char*iconName=winName;
394     qx->x = posx;
395     qx->y = posy;
396     qx->lenx=w;
397     qx->leny=h;
398
399     qx->mywin=0;
400     qx->mywin=XCreateSimpleWindow(X.d,X.root,posx,posy,qx->lenx,qx->leny,0,X.white,X.black);
401
402     if(!qx->mywin) {
403         printf("Error: Couldn't create SimpleWindow\n");
404         pthread_mutex_unlock(&X.xmutex);
405         return 0;
406     }
407     qx->xwmh=XAllocWMHints();
408     qx->xsh=XAllocSizeHints();
409     qx->xch=XAllocClassHint();
410
411     qx->xsh->flags=(PPosition|PSize|PMinSize|PMaxSize);
412     qx->xsh->height=qx->xsh->min_height=qx->xsh->max_height=h;
413     qx->xsh->width =qx->xsh->min_width =qx->xsh->max_width =w;
414     qx->xsh->x=0;
415     qx->xsh->y=0;
416
417     if(!XStringListToTextProperty(&winName, 1, &wname)) {
418         printf("Error: XStringToTextProperty failed\n");
419         pthread_mutex_unlock(&X.xmutex);
420         return 0;
421     }
422     if(!XStringListToTextProperty(&iconName, 1, &iname)) {
423         printf("Error: XStringToTextProperty failed\n");
424         pthread_mutex_unlock(&X.xmutex);
425         return 0;
426     }
427
428     XSetWMProperties(X.d,qx->mywin,&wname,&iname,0,0,qx->xsh,qx->xwmh,qx->xch);
429
430     qx->xgcv.foreground=X.white;
431     qx->xgcv.background=X.black;
432     //qx->gc=XDefaultGC(X.d,qx->mywin);
433     qx->gc=XCreateGC(X.d,qx->mywin,GCForeground|GCBackground,&qx->xgcv);
434
435     XGetWindowAttributes(X.d,qx->mywin,&qx->xwa);
436
437     // enable events for close window
438     XChangeProperty(X.d, qx->mywin, X.atom_wm, XA_ATOM, 32, PropModeReplace, (unsigned char *)(&X.atom_wmdelete), 1 );
439
440     // no border
441     // (This code comes from the project xine (xine.sourceforge.net)
442     //  I wonder if this works with all windowmanagers, though)
443     if(noborder>0)
444     {
445         Atom atomprop = XInternAtom(X.d, "_MOTIF_WM_HINTS", True);
446         if(atomprop)
447         {
448             MWMHints mwmhints;
449             mwmhints.flags = MWM_HINTS_DECORATIONS;
450             if(noborder==1) mwmhints.decorations = 2;
451             if(noborder>1) mwmhints.decorations = 0;
452             XChangeProperty(X.d, qx->mywin, atomprop, atomprop, 32,
453                             PropModeReplace, (unsigned char *) &mwmhints, 5);
454             XSetTransientForHint (X.d, qx->mywin, X.root);
455         }
456     }
457
458     qx->bpp=qx->xwa.depth;
459     if(qx->bpp==24) qx->bypp=4; //Can this be 3, also?
460     if(qx->bpp==16) qx->bypp=2;
461     if(qx->bpp==8) qx->bypp=1;
462
463 #ifdef USE_SHM
464     XShmSegmentInfo segInfo;
465     int bpl = qx->lenx*qx->bypp;
466     segInfo.readOnly = 0;
467     segInfo.shmid = shmget(IPC_PRIVATE,qx->leny*bpl,IPC_CREAT|0777);
468     struct shmid_ds buf;
469     if (segInfo.shmid <0) {
470         perror("shmget");
471         fprintf(stderr,"Size = %d x %d\n", qx->lenx, qx->leny);
472     }
473     segInfo.shmaddr = (char*)shmat (segInfo.shmid, 0, 0);
474     if ((long)segInfo.shmaddr == -1) {
475         perror("shmat");
476     }
477     XShmAttach(X.d, &segInfo);
478     if (shmctl(segInfo.shmid, IPC_RMID, &buf) < 0) 
479         perror("shmctl");
480
481     qx->currentscr = (U8*)segInfo.shmaddr;
482
483 # ifdef USE_PIXMAP
484     qx->offscreen_pixmap = XShmCreatePixmap(X.d,qx->mywin,segInfo.shmaddr,&segInfo,qx->lenx,qx->leny,DefaultDepth(X.d, DefaultScreen(X.d)));
485 # else 
486     qx->offscreen_ximage = XShmCreateImage(X.d,X.v,24,ZPixmap,(char*)segInfo.shmaddr, &segInfo, qx->lenx,qx->leny);
487     XInitImage(qx->offscreen_ximage);
488 # endif
489
490 #else
491
492     qx->currentscr = malloc(qx->lenx*qx->leny*4);
493 # ifdef USE_PIXMAP
494     qx->offscreen_pixmap = XCreatePixmapFromBitmapData(X.d,qx->mywin,qx->currentscr,qx->lenx,qx->leny,0,0,DefaultDepth(X.d,X.s));
495 # else
496     qx->offscreen_ximage = XCreateImage(X.d,X.v,24,ZPixmap,0,(char*)qx->currentscr,qx->lenx,qx->leny,8,qx->lenx*4);
497     XInitImage(qx->offscreen_ximage);
498 # endif
499
500 #endif
501     XSync(X.d, False);
502
503     /* get the image data for the area this window will be/is placed on */
504     /*{
505         Window tmpwin;
506         XImage*background;
507         int x,y;
508         XTranslateCoordinates(X.d,qx->mywin,X.root,0,0,&x,&y,&tmpwin);
509         qx->background = XGetImage(X.d, DefaultRootWindow(X.d), x, y, qx->lenx, qx->leny, AllPlanes, ZPixmap);
510         printf("%d %d\n",x,y);
511     }*/
512     
513     XMapRaised(X.d,qx->mywin);
514     XFlush(X.d);
515
516     qx->xswa.event_mask=(qx->xwa.your_event_mask|=
517             ButtonPressMask|
518             ButtonReleaseMask|
519             //ExposureMask|
520             KeyPressMask|
521             MotionNotify|
522             EnterWindowMask|
523             LeaveWindowMask|
524             PointerMotionMask
525         );
526     XChangeWindowAttributes(X.d,qx->mywin,CWEventMask,&qx->xswa);
527
528     qx->queue = queue_init(sizeof(gfxevent_t), 256);
529     
530     pthread_mutex_unlock(&X.xmutex);
531         
532     pthread_mutex_init(&qx->wmutex, 0);
533     return qx;
534 }
535
536 static void closeWindow(QXWindow*qx)
537 {
538     if(qx->mywin) {
539         pthread_mutex_lock(&X.xmutex);
540         XDestroyWindow(X.d,qx->mywin);
541         pthread_mutex_unlock(&X.xmutex);
542         qx->mywin = 0;
543     }
544 }
545
546 static void processEvent(gfxevent_t*event)
547 {
548     fd_set fdset;
549     struct timeval de;
550     int status;
551
552     XEvent xe;
553
554     FD_ZERO(&fdset);
555     FD_SET(ConnectionNumber(X.d),&fdset);
556     de.tv_sec = 0;
557     de.tv_usec = 5000; // 1/200 s
558
559     pthread_mutex_lock(&X.xmutex);
560     FD_ZERO(&fdset);
561     FD_SET(ConnectionNumber(X.d),&fdset);
562     de.tv_sec = de.tv_usec = 0;
563     if(!select(ConnectionNumber(X.d)+1, &fdset, 0, 0, &de)) {
564         pthread_mutex_unlock(&X.xmutex);
565         usleep(1000);
566         return;
567     }
568     XNextEvent(X.d,&xe);
569     pthread_mutex_unlock(&X.xmutex);
570     
571     /* 1: find out which windows the message is for */
572     Window w = 0;
573     switch(xe.type)
574     {
575         case ClientMessage: w = xe.xclient.window;break;
576         case Expose:        w = xe.xexpose.window;break;
577         case NoExpose:      w = xe.xexpose.window;break;
578         case ButtonPress:   w = xe.xbutton.window;break;
579         case ButtonRelease: w = xe.xbutton.window;break;
580         case KeyPress:      w = xe.xkey.window;   break;
581         case KeyRelease:    w = xe.xkey.window;   break;
582         case MotionNotify:  w = xe.xmotion.window;break;
583         case EnterNotify:   w = xe.xmotion.window;break;
584         case LeaveNotify:   w = xe.xmotion.window;break;
585         default:
586         /* this usually happens for unknown events which
587            we don't care about */
588             return;
589     }
590
591     QXWindow*qx=X.windowring;
592     QXWindow*win=0;
593     event->internal = 0;
594     do {
595         if(w == qx->mywin) {
596             win = qx;
597         }
598     }
599     while(qx!=X.windowring);
600     
601     if(!win) {
602         /* Not one of our windows. This may be a bug */
603         return;
604     }
605     event->internal = win;
606
607     /* 2: Go for the event construction */
608
609     switch(xe.type)
610     {
611         case ButtonPress:
612             event->type = GFXEVENT_MOUSEPRESS;
613             event->button = xe.xbutton.button;
614         break;
615         case ButtonRelease:
616             event->type = GFXEVENT_MOUSERELEASE;
617             event->button = xe.xbutton.button;
618         break;
619         case KeyPress:
620             event->type = GFXEVENT_KEYPRESS;
621             event->key = xe.xkey.keycode;
622         break;
623         case KeyRelease:
624             event->type = GFXEVENT_KEYRELEASE;
625             event->key = xe.xkey.keycode;
626         break;
627         case MotionNotify:
628             event->type = GFXEVENT_MOUSEMOVE;
629             pthread_mutex_lock(&qx->wmutex);
630             event->x = xe.xmotion.x_root - win->x;
631             event->y = xe.xmotion.y_root - win->y;
632             if(!queue_put(win->queue, event)) {
633                 printf("Queue overflow for window %08x!\n", win);
634             }
635             pthread_mutex_unlock(&qx->wmutex);
636         break;
637         case EnterNotify:
638             event->type = GFXEVENT_MOUSEENTER;
639             pthread_mutex_lock(&qx->wmutex);
640             event->x = xe.xmotion.x_root - win->x;
641             event->y = xe.xmotion.y_root - win->y;
642             pthread_mutex_unlock(&qx->wmutex);
643         break;
644         case LeaveNotify:
645             event->type = GFXEVENT_MOUSELEAVE;
646             pthread_mutex_lock(&qx->wmutex);
647             event->x = xe.xmotion.x - win->x;
648             event->y = xe.xmotion.y - win->y;
649             pthread_mutex_unlock(&qx->wmutex);
650         break;
651         case ClientMessage:
652             if ((xe.xclient.message_type == X.atom_wm ) &&
653                 ((unsigned)xe.xclient.data.l[0] == X.atom_wmdelete) ) {
654                 pthread_mutex_lock(&qx->wmutex);
655                 closeWindow(win);
656                 event->type = GFXEVENT_DESTROY;
657                 pthread_mutex_unlock(&qx->wmutex);
658             }
659         break;
660     }
661 }
662
663 static void*eventloop(void*r)
664 {
665     //while(!XEventsQueued(X.d,QueuedAfterReading))
666     while(X.running)
667     {
668         gfxevent_t event;
669         event.type = GFXEVENT_NOTHING;
670         event.key = event.x = event.y = event.button = -1;
671         processEvent(&event);
672         if(event.type != GFXEVENT_NOTHING && event.type != GFXEVENT_MOUSEMOVE) {
673             QXWindow*win = (QXWindow*)event.internal;
674             pthread_mutex_lock(&win->wmutex);
675             if(!queue_put(win->queue,&event)) {
676                 fprintf(stderr, "Queue overflow for window %08x!\n", win);
677             }
678             pthread_mutex_unlock(&win->wmutex);
679         }
680     }
681     return 0;
682 }
683
684 typedef struct _internal {
685     GC gc;
686     char*name;
687     QXWindow*qx;
688     int lenx, leny;
689     int tmplenx, tmpleny;
690     void*currentscr2;
691 } internal_t;
692
693 static void gfxwindow_on(gfxwindow_t*win)
694 {
695     internal_t*i = (internal_t*)win->internal;
696          
697     i->qx=openwindow(64, 64, win->width, win->height, i->name);
698     if(!i->qx)
699         return;
700     i->currentscr2=0;
701     if(i->qx->bypp != 4) {
702         fprintf(stderr, "Warning: Not a native 32 bit screen, using second buffer\n");
703         i->currentscr2 = malloc(win->width*win->height*4);
704         win->currentscr = (unsigned char*)i->currentscr2;
705     } else {
706         win->currentscr = i->qx->currentscr;
707     }
708     win->lastscr = 0;
709 }
710
711 static void gfxwindow_off(gfxwindow_t*win)
712 {
713     internal_t*i = (internal_t*)win->internal;
714     if(i->currentscr2)
715         free(i->currentscr2);
716     closeWindow(i->qx);
717 }
718
719 static void gfxwindow_flippage(gfxwindow_t*win)
720 {
721     internal_t*i = (internal_t*)win->internal;
722     pthread_mutex_lock(&X.xmutex);
723
724     if(i->currentscr2) {
725         if(i->qx->bypp==2 && X.isnative16) {
726             int t;
727             int l = win->width*win->height;
728             unsigned char*dest = i->qx->currentscr;
729             unsigned char*src = (unsigned char*)i->currentscr2;
730             do {
731                 dest[1] = (src[2]&0xf8)|(src[1]>>5);
732                 dest[0] = ((src[1]<<5)&0xe0)|(src[0]>>3);
733                 dest+=2;
734                 src+=4;
735             } while(--l);
736         } else {
737             memcpy(i->qx->currentscr, i->currentscr2, i->lenx*i->leny*i->qx->bypp);
738         }
739     }
740
741     XSetFunction(X.d,i->qx->gc,GXcopy);
742 #ifdef USE_PIXMAP
743 # ifndef USE_SHM
744     if(i->qx->offscreen_pixmap) {
745         XFreePixmap(X.d,i->qx->offscreen_pixmap);i->qx->offscreen_pixmap = 0;
746     }
747     i->qx->offscreen_pixmap = XCreatePixmapFromBitmapData(X.d,i->qx->mywin,i->qx->currentscr,i->qx->lenx,i->qx->leny,X.white,X.black,DefaultDepth(X.d,X.s));
748 # endif
749     XCopyArea(X.d,i->qx->offscreen_pixmap,i->qx->mywin,i->qx->gc, 0, 0, i->qx->lenx,i->qx->leny, 0, 0);
750     XFlush(X.d);
751 #else
752     XPutImage(X.d,i->qx->mywin,i->qx->gc,i->qx->offscreen_ximage,0,0,0,0,i->qx->lenx,i->qx->leny);
753 #endif
754     pthread_mutex_unlock(&X.xmutex);
755 }
756
757 void gfxwindow_setcolor(gfxwindow_t*win, U32 c)
758 {
759     internal_t*i = (internal_t*)win->internal;
760     pthread_mutex_lock(&X.xmutex);
761     i->qx->xgcv.foreground=getColorFromRGB((U8)(c>>16),(U8)(c>>8),(U8)c);
762     i->gc=XCreateGC(X.d,i->qx->mywin,GCForeground|GCBackground,&i->qx->xgcv);
763     pthread_mutex_unlock(&X.xmutex);
764 }
765 void gfxwindow_putpixel(gfxwindow_t*win, int x, int y, unsigned char c)
766 {
767     internal_t*i = (internal_t*)win->internal;
768     if(((U32)x)<(U32)win->width && ((U32)y)<(U32)win->height)
769         *(U32*)&win->currentscr[y*win->width*i->qx->bypp+x*i->qx->bypp]=c;
770 }
771 static gfxevent_t gfxwindow_getEvent(gfxwindow_t*win)
772 {
773     internal_t*i = (internal_t*)win->internal;
774     gfxevent_t event;
775     pthread_mutex_lock(&i->qx->wmutex);
776     if(!i->qx->queue || !queue_get(i->qx->queue,&event)) {
777         event.type = GFXEVENT_NOTHING;
778     } else if(event.type == GFXEVENT_DESTROY) {
779         queue_destroy(i->qx->queue);i->qx->queue=0;
780     }
781     pthread_mutex_unlock(&i->qx->wmutex);
782     return event;
783 }
784    
785 static void gfxwindow_move(gfxwindow_t*win, int x,int y)
786 {
787     internal_t*i = (internal_t*)win->internal;
788     pthread_mutex_lock(&i->qx->wmutex);
789     pthread_mutex_lock(&X.xmutex);
790     XWindowAttributes a;
791     i->qx->x += x;
792     i->qx->y += y;
793     XMoveResizeWindow(X.d, i->qx->mywin, i->qx->x, i->qx->y, i->tmplenx, i->tmpleny);
794
795     queue_t* queue2 = queue_init(sizeof(gfxevent_t), 256);
796     gfxevent_t event;
797     /* HACK: now that we have moved the window, all mouse move events
798        are outdated- remove them*/
799     while(queue_get(i->qx->queue, &event)) {
800         if(event.type!=GFXEVENT_MOUSEMOVE)
801             queue_put(queue2, &event);
802     }
803     queue_destroy(i->qx->queue);
804     i->qx->queue = queue2;
805
806     pthread_mutex_unlock(&X.xmutex);
807     pthread_mutex_unlock(&i->qx->wmutex);
808 }
809
810 static void gfxwindow_resize(gfxwindow_t*win, int x,int y)
811 {
812     internal_t*i = (internal_t*)win->internal;
813     if(x>win->width || y>win->height) {
814         fprintf(stderr, "resize: can only make windows smaller\n");
815         return;
816     }
817     if(x<1) x=1;
818     if(y<1) y=1;
819     pthread_mutex_lock(&i->qx->wmutex);
820     i->tmplenx=x;
821     i->tmpleny=y;
822     pthread_mutex_lock(&X.xmutex);
823     XMoveResizeWindow(X.d, i->qx->mywin, i->qx->x, i->qx->y, i->tmplenx, i->tmpleny);
824     pthread_mutex_unlock(&X.xmutex);
825     pthread_mutex_unlock(&i->qx->wmutex);
826 }
827 static void gfxwindow_destroy(gfxwindow_t*win)
828 {
829     internal_t*i = (internal_t*)win->internal;
830     gfxwindow_off(win);
831     pthread_mutex_destroy(&i->qx->wmutex);
832 }
833
834 gfxwindow_t* gfxwindow_new(int width, int height)
835 {
836     gfxwindow_t*w = (gfxwindow_t*)malloc(sizeof(gfxwindow_t));
837     if(!initializexwindows()) {
838         fprintf(stderr, "Warning: Couldn't initialize X-Windows\n");
839     }
840     internal_t*i = malloc(sizeof(internal_t));
841     w->internal = i;
842     w->move = gfxwindow_move;
843     w->resize = gfxwindow_resize;
844     w->flippage = gfxwindow_flippage;
845     w->getEvent = gfxwindow_getEvent;
846     w->width = width;
847     w->height = height;
848     w->destroy = gfxwindow_destroy;
849
850     i->lenx = i->tmplenx = width;
851     i->leny = i->tmpleny = height;
852     i->name = "";
853
854     gfxwindow_on(w);
855     return w;
856 };
857