aboutsummaryrefslogtreecommitdiff
path: root/Src/Wasabi/bfc/platform/linux
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Wasabi/bfc/platform/linux')
-rw-r--r--Src/Wasabi/bfc/platform/linux/linux.cpp961
1 files changed, 961 insertions, 0 deletions
diff --git a/Src/Wasabi/bfc/platform/linux/linux.cpp b/Src/Wasabi/bfc/platform/linux/linux.cpp
new file mode 100644
index 00000000..67471d4a
--- /dev/null
+++ b/Src/Wasabi/bfc/platform/linux/linux.cpp
@@ -0,0 +1,961 @@
+#include <precomp.h>
+
+#include <api/api.h>
+ #include <api/linux/api_linux.h>
+
+#include <bfc/ptrlist.h>
+#include <bfc/string/string.h>
+#include <bfc/critsec.h>
+#include <bfc/thread.h>
+#include <api/application/ipcs.h>
+
+#ifdef WASABI_COMPILE_WND
+Display *Linux::display = NULL;
+
+int linux_atoms_loaded = 0;
+
+Atom winamp_msg;
+Atom dnd_enter, dnd_position, dnd_status, dnd_leave, dnd_drop, dnd_finished;
+Atom dnd_selection, dnd_wa3drop, dnd_private, dnd_typelist;
+Atom dnd_urilist, dnd_textplain, dnd_mozurl;
+#endif
+
+#ifndef _NOSTUDIO
+
+#ifdef WASABI_COMPILE_WND
+void LoadAtoms() {
+ if ( !linux_atoms_loaded ) {
+ linux_atoms_loaded = 1;
+ winamp_msg = XInternAtom( Linux::getDisplay(), "Winamp3", False );
+ dnd_wa3drop = XInternAtom( Linux::getDisplay(), "Winamp3_drop", False );
+ dnd_enter = XInternAtom( Linux::getDisplay(), "XdndEnter", True );
+ dnd_position = XInternAtom( Linux::getDisplay(), "XdndPosition", True );
+ dnd_status = XInternAtom( Linux::getDisplay(), "XdndStatus", True );
+ dnd_leave = XInternAtom( Linux::getDisplay(), "XdndLeave", True );
+ dnd_drop = XInternAtom( Linux::getDisplay(), "XdndDrop", True );
+ dnd_finished = XInternAtom( Linux::getDisplay(), "XdndFinished", True );
+ dnd_selection = XInternAtom( Linux::getDisplay(), "XdndSelection", True );
+ dnd_private = XInternAtom( Linux::getDisplay(), "XdndActionPrivate", True );
+ dnd_typelist = XInternAtom( Linux::getDisplay(), "XdndTypeList", True );
+ dnd_urilist = XInternAtom( Linux::getDisplay(), "text/uri-list", True );
+ dnd_textplain = XInternAtom( Linux::getDisplay(), "text/plain", True );
+ dnd_mozurl = XInternAtom( Linux::getDisplay(), "text/x-moz-url", True );
+ }
+}
+#endif
+
+#endif
+
+void OutputDebugString( const char *s ) {
+#ifdef _DEBUG
+ fprintf( stderr, "%s", s );
+#endif
+ char *file = getenv( "WASABI_LOG_FILE" );
+ if ( file ) {
+ if ( !STRCMP( file, "-" ) ) {
+ fprintf( stdout, "%s", s );
+ } else {
+ FILE *f = fopen( file, "a" );
+ if ( f ) {
+ fprintf( f, "%s", s );
+ fclose( f );
+ }
+ }
+ }
+}
+
+DWORD GetTickCount() {
+ static int starttime = -1;
+
+ if ( starttime == -1 )
+ starttime = time( NULL );
+
+ struct timeb tb;
+ ftime( &tb );
+ tb.time -= starttime;
+ return tb.time * 1000 + tb.millitm;
+}
+void Sleep( int ms ) {
+ if ( ms != 0 ) {
+ struct timespec ts = { 0, 0 };
+ ts.tv_sec = ms / 1000;
+ ts.tv_nsec = (ms % 1000) * 1000000;
+ nanosleep( &ts, NULL);
+ // usleep(ms * 1000);
+ }
+}
+
+#ifndef _NOSTUDIO
+
+#ifdef WASABI_COMPILE_WND
+
+Display *Linux::getDisplay() {
+ if ( ! display )
+ display = WASABI_API_LINUX->linux_getDisplay();
+
+ return display;
+}
+
+XContext Linux::getContext() {
+ static XContext context = 0;
+
+ if ( context == 0 )
+ context = WASABI_API_LINUX->linux_getContext();
+
+ return context;
+}
+
+int Linux::getScreenNum() { return DefaultScreen( getDisplay() ); }
+
+Window Linux::RootWin() {
+ return RootWindow( getDisplay(), getScreenNum() );
+}
+Visual *Linux::DefaultVis() {
+ return DefaultVisual( getDisplay(), getScreenNum() );
+}
+
+void Linux::setCursor( HWND h, int cursor ) {
+ Cursor c = XCreateFontCursor( Linux::getDisplay(), cursor );
+
+ if ( cursor == None )
+ XUndefineCursor( Linux::getDisplay(), h );
+ else
+ XDefineCursor( Linux::getDisplay(), h, c );
+
+ XFreeCursor( Linux::getDisplay(), c );
+}
+
+int Linux::convertEvent( MSG *m, XEvent *e ) {
+ m->hwnd = e->xany.window;
+
+ if ( m->hwnd ) {
+ api_window *rw =(api_window *)GetWindowLong( m->hwnd, GWL_USERDATA );
+ if ( !rw ) {
+ // This is to fix messages for dead windows...
+ return 0;
+ }
+ }
+
+ switch ( e->type ) {
+ case ButtonPress:
+ switch( e->xbutton.button ) {
+ case 1:
+ m->message = WM_LBUTTONDOWN;
+ m->lParam = (e->xbutton.x & 0xffff) | (e->xbutton.y << 16);
+ break;
+
+ case 2:
+
+ case 3:
+ m->message = WM_RBUTTONDOWN;
+ m->lParam = (e->xbutton.x & 0xffff) | (e->xbutton.y << 16);
+ break;
+
+ case 4:
+ m->message = WM_MOUSEWHEEL;
+ m->wParam = 120 << 16 | 0; // 1 tick, no modifiers
+ m->lParam = (e->xbutton.x & 0xffff) | (e->xbutton.y << 16);
+ break;
+
+ case 5:
+ m->message = WM_MOUSEWHEEL;
+ m->wParam = (-120) << 16 | 0; // 1 tick, no modifiers
+ m->lParam = (e->xbutton.x & 0xffff) | (e->xbutton.y << 16);
+ break;
+ }
+ break;
+ case ButtonRelease:
+ switch( e->xbutton.button ) {
+ case 1:
+ m->message = WM_LBUTTONUP;
+ m->lParam = (e->xbutton.x & 0xffff) | (e->xbutton.y << 16);
+ break;
+
+ case 2:
+
+ case 3:
+ m->message = WM_RBUTTONUP;
+ m->lParam = (e->xbutton.x & 0xffff) | (e->xbutton.y << 16);
+ break;
+ }
+ break;
+ case MotionNotify:
+ {
+ m->message = WM_MOUSEMOVE;
+ do {
+ // Spin...
+ } while( XCheckTypedWindowEvent( Linux::getDisplay(), m->hwnd, MotionNotify, e ) );
+
+ RECT r;
+ POINT offset = {0, 0};
+ HWND hwnd = m->hwnd;
+
+ GetWindowRect( hwnd, &r );
+
+ m->lParam = ((e->xmotion.x_root - r.left) & 0xffff) |
+ ((e->xmotion.y_root - r.top) << 16);
+
+ if ( ! (e->xmotion.state & ( Button1Mask | Button2Mask | Button3Mask )) )
+ PostMessage( m->hwnd, WM_SETCURSOR, m->hwnd, 0 );
+ }
+ break;
+
+ case KeyPress:
+ m->message = WM_KEYDOWN;
+ m->wParam = e->xkey.keycode;
+ break;
+
+ case KeyRelease:
+ m->message = WM_KEYUP;
+ m->wParam = e->xkey.keycode;
+ break;
+
+ case Expose:
+ {
+ RECT r;
+ m->message = WM_PAINT;
+ do {
+ r.left = e->xexpose.x;
+ r.top = e->xexpose.y;
+ r.right = r.left + e->xexpose.width;
+ r.bottom = r.top + e->xexpose.height;
+ InvalidateRect( m->hwnd, &r, FALSE );
+ } while( XCheckTypedWindowEvent( Linux::getDisplay(), m->hwnd, Expose, e ) );
+ }
+ break;
+
+ case ClientMessage: {
+ static int coord = -1;
+ static Atom supported = None;
+ XClientMessageEvent cme;
+
+ LoadAtoms();
+
+ int message = e->xclient.message_type;
+ if ( message == dnd_enter ) {
+ if ( e->xclient.data.l[1] & 1 ) {
+ Atom actual;
+ int format;
+ long unsigned int nitems, bytes;
+ unsigned char *data = NULL;
+
+ XGetWindowProperty( Linux::getDisplay(), e->xclient.data.l[0],
+ dnd_typelist, 0, 65536, True, XA_ATOM,
+ &actual, &format, &nitems, &bytes, &data );
+
+ Atom *atomdata = (Atom *)data;
+
+ supported = None;
+ for( int i = 0; i < nitems; i++ ) {
+ if ( atomdata[i] == dnd_urilist ) {
+ supported = dnd_urilist;
+ }
+ }
+ if ( supported == None ) {
+ for( int i = 0; i < nitems; i++ ) {
+ if ( atomdata[i] == dnd_textplain ) {
+ OutputDebugString( "text/plain found\n" );
+ supported = dnd_textplain;
+ }
+ }
+ }
+ if ( supported == None ) {
+ for( int i = 0; i < nitems; i++ ) {
+ if ( atomdata[i] == dnd_mozurl ) {
+ supported = dnd_mozurl;
+ }
+ }
+ }
+
+ XFree( data );
+ } else {
+ if ( e->xclient.data.l[2] == dnd_urilist ||
+ e->xclient.data.l[3] == dnd_urilist ||
+ e->xclient.data.l[4] == dnd_urilist ) {
+ supported = dnd_urilist;
+ } else if ( e->xclient.data.l[2] == dnd_mozurl ||
+ e->xclient.data.l[3] == dnd_mozurl ||
+ e->xclient.data.l[4] == dnd_mozurl ) {
+ supported = dnd_mozurl;
+ }
+ }
+
+
+ // DnD Enter
+ return 0;
+
+ } else if ( message == dnd_position ) {
+ // DnD Position Notify
+
+ cme.type = ClientMessage;
+ cme.message_type = dnd_status;
+ cme.format = 32;
+ cme.window = e->xclient.data.l[0];
+ cme.data.l[0] = e->xclient.window;
+ cme.data.l[1] = 1; // Can Accept
+ cme.data.l[2] = cme.data.l[3] = 0; // Empty rectangle - give us moves
+ cme.data.l[4] = dnd_private; // We're doing our own thing
+
+ if ( coord == -1 && supported != None ) {
+ XConvertSelection( Linux::getDisplay(), dnd_selection, supported,
+ dnd_wa3drop, cme.window, CurrentTime );
+ }
+
+ coord = e->xclient.data.l[2];
+
+ XSendEvent( Linux::getDisplay(), e->xclient.data.l[0], False,
+ NoEventMask, (XEvent *)&cme );
+
+ return 0;
+
+ } else if ( message == dnd_leave ) {
+ // DnD Leave
+ coord = -1;
+ supported = None;
+
+ return 0;
+
+ } else if ( message == dnd_drop ) {
+ // DnD Drop
+
+ Window win = e->xclient.data.l[0];
+
+ cme.type = ClientMessage;
+ cme.message_type = dnd_finished;
+ cme.format = 32;
+ cme.window = e->xclient.data.l[0];
+ cme.data.l[0] = e->xclient.window;
+ cme.data.l[1] = cme.data.l[2] = cme.data.l[3] = cme.data.l[4] = 0;
+
+ XSendEvent( Linux::getDisplay(), e->xclient.data.l[0], False,
+ NoEventMask, (XEvent *)&cme );
+
+ if ( supported != None ) {
+ Atom actual;
+ int format;
+ long unsigned int nitems, bytes;
+ unsigned char *data = NULL;
+
+ XGetWindowProperty( Linux::getDisplay(), cme.window, dnd_wa3drop,
+ 0, 65536, True, supported, &actual,
+ &format, &nitems, &bytes,
+ &data );
+
+ OutputDebugString( StringPrintf( "Drop data (%d):\n%s\n", nitems, data ) );
+
+ m->message = WM_DROPFILES;
+ m->wParam = coord;
+ m->lParam = (LPARAM)data;
+
+ coord = -1;
+ supported = None;
+
+ } else {
+ coord = -1;
+ supported = None;
+ return 0;
+ }
+
+ break;
+
+ } else if ( message == winamp_msg ) {
+ // Internal Message ...
+
+ m->message = e->xclient.data.l[0];
+ m->wParam = e->xclient.data.l[1];
+ m->lParam = e->xclient.data.l[2];
+ break;
+
+ } else {
+ return 0;
+ }
+ break;
+ }
+
+ case LeaveNotify:
+ case EnterNotify:
+ m->message = WM_MOUSEMOVE;
+ m->lParam = (e->xcrossing.x & 0xffff) | (e->xcrossing.y << 16);
+
+ if ( ! (e->xcrossing.state & ( Button1Mask | Button2Mask | Button3Mask )) )
+ PostMessage( m->hwnd, WM_SETCURSOR, m->hwnd, 0 );
+ break;
+
+ case FocusIn:
+ m->message = WM_SETFOCUS;
+ break;
+
+ case FocusOut:
+ m->message = WM_KILLFOCUS;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static HWND activeWindow;
+
+HWND GetActiveWindow() {
+ return activeWindow;
+}
+
+int IntersectRect( RECT *out, const RECT *i1, const RECT *i2 ) {
+ return Std::rectIntersect(i1, i2, out);
+}
+
+void TranslateMessage( MSG *m ) {
+ if ( m->message != WM_CHAR && m->message != WM_KEYDOWN &&
+ m->message != WM_KEYUP )
+ return;
+
+ int index = !!( Std::keyDown( VK_SHIFT ));
+
+ m->wParam = XKeycodeToKeysym( Linux::getDisplay(), m->wParam, index );
+}
+
+void PostMessage( HWND win, UINT msg, WPARAM wParam, LPARAM lParam ) {
+ XEvent e;
+
+ LoadAtoms();
+
+ e.type = ClientMessage;
+ e.xclient.window = win;
+ e.xclient.message_type = winamp_msg;
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = msg;
+ e.xclient.data.l[1] = wParam;
+ e.xclient.data.l[2] = lParam;
+
+ XSendEvent( Linux::getDisplay(), win, FALSE, NoEventMask, &e );
+}
+
+void PostQuitMessage( int i ) {
+ PostMessage( None, WM_QUIT, i, 0 );
+}
+
+#endif // wnd
+
+#if defined(WASABI_API_TIMER) | defined(WASABI_API_WND)
+
+struct TimerElem {
+ HWND win;
+ int id;
+ int nexttime;
+ int delta;
+ TIMERPROC tproc;
+
+ TimerElem( HWND _win, int _id, int ms, TIMERPROC _tproc ) {
+ win = _win;
+ id = _id;
+ delta = ms;
+ tproc = _tproc;
+ nexttime = Std::getTickCount() + delta;
+ }
+};
+
+int timer_id = 0;
+CriticalSection timer_cs;
+PtrList<TimerElem> timer_elems;
+
+int SetTimer( HWND win, int id, int ms, TIMERPROC tproc ) {
+ KillTimer(win, id);
+
+ if ( win == (HWND)0 ) {
+ id = timer_id++;
+ }
+
+ TimerElem *te = new TimerElem( win, id, ms, tproc );
+ timer_cs.enter();
+ timer_elems.addItem( te, PTRLIST_POS_LAST );
+ timer_cs.leave();
+
+ return id;
+}
+
+void KillTimer( HWND win, int id ) {
+ timer_cs.enter();
+ for( int i = 0; i < timer_elems.getNumItems(); i++ )
+ if ( timer_elems[i]->win == win && timer_elems[i]->id == id ) {
+ delete timer_elems[i];
+ timer_elems.delByPos( i );
+ i--;
+ }
+ timer_cs.leave();
+}
+
+CriticalSection send_cs;
+MSG *send_msg;
+int sending = 0;
+int send_ret;
+pthread_t message_thread = (pthread_t)-1;
+
+int _GetMessage( MSG *m, HWND, UINT, UINT, int block=1) {
+ MEMSET( m, 0, sizeof( MSG ) );
+
+ message_thread = pthread_self();
+
+#ifdef WASABI_COMPILE_WND
+ XEvent e;
+#endif // wnd
+ int curtime;
+ int done = 0;
+ int first = 1;
+ static wa_msgbuf ipcm;
+ static int qid = -1;
+ int size;
+
+ if ( qid == -1 ) { qid = WASABI_API_LINUX->linux_getIPCId(); }
+
+ if ( sending ) {
+ *m = *send_msg;
+ done = 1;
+ }
+
+ while( !done && (block || first)) {
+ if ( qid != -1 ) {
+ if ( (size = msgrcv( qid, &ipcm, IPC_MSGMAX , 0, IPC_NOWAIT )) != -1 ) {
+ m->hwnd = None;
+ m->message = WM_WA_IPC;
+ m->wParam = (WPARAM)&ipcm;
+ break;
+ }
+ }
+
+ curtime = GetTickCount();
+
+ timer_cs.enter();
+ for( int i = 0; i < timer_elems.getNumItems(); i++ ) {
+ if ( timer_elems[i]->nexttime < curtime ) {
+ if (block)
+ while( timer_elems[i]->nexttime < curtime )
+ timer_elems[i]->nexttime += timer_elems[i]->delta;
+
+ m->hwnd = timer_elems[i]->win;
+ m->message = WM_TIMER;
+ m->wParam = (WPARAM)timer_elems[i]->id;
+ m->lParam = (LPARAM)timer_elems[i]->tproc;
+
+ done = 1;
+ }
+ }
+ timer_cs.leave();
+
+ if ( !done && ! first )
+ Sleep( 1 );
+ else
+ first = 0;
+
+#ifdef WASABI_API_WND
+ if ( !done && XPending( Linux::getDisplay() ) ) {
+ int n = XEventsQueued( Linux::getDisplay(), QueuedAlready );
+
+ for ( int i = 0; !done && i < n; i++ ) {
+ XNextEvent( Linux::getDisplay(), &e );
+ if ( Linux::convertEvent( m, &e ) )
+ done = 1;
+ }
+ if ( done )
+ break;
+ }
+#endif // wnd
+ }
+
+#ifdef WASABI_API_WND
+ activeWindow = m->hwnd;
+#endif // wnd
+
+ return m->message != WM_QUIT;
+}
+
+int GetMessage( MSG *m, HWND w, UINT f, UINT l) {
+ return _GetMessage(m, w, f, l, 1);
+}
+
+// on linux, we don't really simply peek when PM_NOREMOVE is used,
+// we just don't block, which is the only thing we want to accomplish here
+int PeekMessage( MSG *m, HWND w, UINT f, UINT l, UINT remove) {
+ if (remove == PM_NOREMOVE) return _GetMessage(m, w, f, l, 0);
+ else _GetMessage(m, w, f, l, 1);
+}
+
+
+int DispatchMessage( MSG *m ) {
+ if ( m->message == WM_TIMER && m->hwnd == None ) {
+ TIMERPROC tproc = (TIMERPROC)m->lParam;
+ tproc( m->hwnd, m->message, m->wParam, 0 );
+ return 1;
+ }
+
+ int ret = 0;
+
+#ifdef WASABI_COMPILE_WND
+ api_window *rootwnd = (api_window *)GetWindowLong( m->hwnd, GWL_USERDATA );
+
+ if ( rootwnd ) {
+ ret = rootwnd->wndProc( m->hwnd, m->message, m->wParam, m->lParam );
+ rootwnd->performBatchProcesses();
+ }
+#endif // wnd
+
+ if ( sending ) {
+ send_ret = ret;
+ sending = 0;
+ }
+
+ return ret;
+}
+
+int SendMessage( HWND win, UINT msg, WPARAM wParam, LPARAM lParam ) {
+ MSG m;
+ m.hwnd = win;
+ m.message = msg;
+ m.wParam = wParam;
+ m.lParam = lParam;
+
+ int ret;
+
+ if ( pthread_equal( message_thread, pthread_self() ) ) {
+ return DispatchMessage( &m );
+
+ } else {
+ send_cs.enter();
+ sending = 1;
+ send_msg = &m;
+ while( sending ) { Sleep( 1 ); }
+ ret = send_ret;
+ send_cs.leave();
+
+ return ret;
+ }
+}
+
+#endif // timer | wnd
+
+int MulDiv( int m1, int m2, int d ) {
+ __asm__ volatile (
+ "mov %0, %%eax\n"
+ "mov %1, %%ebx\n"
+ "mov %2, %%ecx\n"
+ "mul %%ebx\n"
+ "div %%ecx\n"
+ : : "m" (m1), "m" (m2), "m" (d)
+ : "%eax", "%ebx", "%ecx", "%edx" );
+}
+
+void ExitProcess( int ret ) {
+ exit( ret );
+}
+
+#ifdef WASABI_COMPILE_WND
+
+void Linux::initContextData( HWND h ) {
+ int *data;
+ XPointer xp;
+
+ ASSERT( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &xp ));
+
+ data = (int *)MALLOC( GWL_ENUM_SIZE * sizeof( int ) );
+
+ data[GWL_HWND] = h;
+
+ XSaveContext( Linux::getDisplay(), h, Linux::getContext(), (char *)data );
+}
+
+void Linux::nukeContextData( HWND h ) {
+ int *data;
+ XPointer xp;
+
+ if ( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &xp ) )
+ return;
+
+ data = (int *)xp;
+
+ ASSERT( data[GWL_HWND] == h );
+
+ if ( data[GWL_INVALIDREGION] ) {
+ XDestroyRegion( (HRGN)data[GWL_INVALIDREGION] );
+ }
+
+ XDeleteContext( Linux::getDisplay(), h, Linux::getContext() );
+
+ FREE( data );
+}
+
+void SetWindowLong( HWND h, contextdata type, LONG value ) {
+ XPointer data;
+
+ if ( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &data ) )
+ return;
+
+ ASSERT( ((int *)data)[GWL_HWND] == h );
+
+ ((int*)data)[type] = value;
+}
+
+LONG GetWindowLong( HWND h, contextdata type ) {
+ XPointer data;
+
+ if ( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &data ) )
+ return 0;
+
+ ASSERT( ((int *)data)[GWL_HWND] == h );
+
+ return ((int*)data)[type];
+}
+
+void MoveWindowRect( HWND h, int x, int y ) {
+ XPointer xp;
+ int *data;
+
+ if ( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &xp ) )
+ return;
+
+ data = (int *)xp;
+
+ ASSERT( data[GWL_HWND] == h );
+
+ data[GWL_RECT_RIGHT] -= data[GWL_RECT_LEFT] - x;
+ data[GWL_RECT_BOTTOM] -= data[GWL_RECT_TOP] - y;
+ data[GWL_RECT_LEFT] = x;
+ data[GWL_RECT_TOP] = y;
+}
+
+void SetWindowRect( HWND h, RECT *r ) {
+ int *data;
+ XPointer xp;
+
+ if ( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &xp ) )
+ return;
+
+ data = (int *)xp;
+
+ ASSERT( data[GWL_HWND] == h );
+
+ data[GWL_RECT_LEFT] = r->left;
+ data[GWL_RECT_TOP] = r->top;
+ data[GWL_RECT_RIGHT] = r->right;
+ data[GWL_RECT_BOTTOM] = r->bottom;
+}
+
+int GetWindowRect( HWND h, RECT *r ) {
+ int *data;
+ XPointer xp;
+
+ if ( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &xp ) )
+ return 0;
+
+ data = (int *)xp;
+
+ ASSERT( data[GWL_HWND] == h );
+
+ r->left = data[GWL_RECT_LEFT];
+ r->top = data[GWL_RECT_TOP];
+ r->right = data[GWL_RECT_RIGHT];
+ r->bottom = data[GWL_RECT_BOTTOM];
+
+ POINT offset = { 0, 0};
+ while( (h = data[GWL_PARENT]) != Linux::RootWin() ) {
+ if ( XFindContext( Linux::getDisplay(), h, Linux::getContext(), &xp ) )
+ return 0;
+
+ data = (int *)xp;
+
+ ASSERT( data[GWL_HWND] == h );
+
+ offset.x += data[GWL_RECT_LEFT];
+ offset.y += data[GWL_RECT_TOP];
+ }
+ r->left += offset.x;
+ r->top += offset.y;
+ r->right += offset.x;
+ r->bottom += offset.y;
+
+ return 1;
+}
+
+int GetUpdateRect( HWND h, RECT *ret, BOOL ) {
+ HRGN invalid = (HRGN)GetWindowLong( h, GWL_INVALIDREGION );
+ if ( ! invalid || XEmptyRegion( invalid ) )
+ return 0;
+
+ XRectangle xr;
+ XClipBox( invalid, &xr );
+ ret->left = xr.x;
+ ret->top = xr.y;
+ ret->right = xr.x + xr.width;
+ ret->bottom = xr.y + xr.height;
+
+ return 1;
+}
+
+void GetUpdateRgn( HWND h, HRGN r, BOOL ) {
+ XSubtractRegion( r, r, r );
+
+ HRGN invalid = (HRGN)GetWindowLong( h, GWL_INVALIDREGION );
+ if ( ! invalid ) return;
+
+
+ XUnionRegion( r, invalid, r );
+
+ XRectangle xr;
+
+ RECT rct;
+ GetWindowRect( h, &rct );
+ xr.x = 0;
+ xr.y = 0;
+ xr.width = rct.right - rct.left;
+ xr.height = rct.bottom - rct.top;
+
+ HRGN tmp = XCreateRegion();
+
+ XUnionRectWithRegion( &xr, tmp, tmp );
+ XIntersectRegion( r, tmp, r );
+ XDestroyRegion( tmp );
+}
+
+void InvalidateRect( HWND h, const RECT *r, BOOL ) {
+ HRGN invalid = (HRGN)GetWindowLong( h, GWL_INVALIDREGION );
+ if ( ! invalid ) {
+ invalid = XCreateRegion();
+ SetWindowLong( h, GWL_INVALIDREGION, (LONG)invalid );
+ }
+
+ XRectangle xr;
+ if ( r == NULL ) {
+ RECT rct;
+ GetWindowRect( h, &rct );
+ xr.x = 0;
+ xr.y = 0;
+ xr.width = rct.right - rct.left;
+ xr.height = rct.bottom - rct.top;
+ } else {
+ xr.x = r->left;
+ xr.y = r->top;
+ xr.width = r->right - r->left;
+ xr.height = r->bottom - r->top;
+ }
+
+ XUnionRectWithRegion( &xr, invalid, invalid );
+
+ PostMessage( h, WM_PAINT, 0, 0 );
+}
+
+void InvalidateRgn( HWND h, HRGN r, BOOL ) {
+ HRGN invalid = (HRGN)GetWindowLong( h, GWL_INVALIDREGION );
+
+ if ( ! invalid ) {
+ invalid = XCreateRegion();
+ SetWindowLong( h, GWL_INVALIDREGION, (LONG)invalid );
+ }
+
+ ASSERT( r != invalid );
+ XUnionRegion( invalid, r, invalid );
+
+ PostMessage( h, WM_PAINT, 0, 0 );
+}
+
+void ValidateRect( HWND h, const RECT *r ) {
+ HRGN invalid = (HRGN)GetWindowLong( h, GWL_INVALIDREGION );
+ if ( ! invalid ) return;
+
+ XRectangle xr;
+ if ( r == NULL ) {
+ XDestroyRegion( invalid );
+ SetWindowLong( h, GWL_INVALIDREGION, 0 );
+ return;
+ }
+
+ xr.x = r->left;
+ xr.y = r->top;
+ xr.width = r->right - r->left;
+ xr.height = r->bottom - r->top;
+
+ HRGN tmp = XCreateRegion();
+ XUnionRectWithRegion( &xr, tmp, tmp );
+ XSubtractRegion( invalid, tmp, invalid );
+ XDestroyRegion( tmp );
+}
+
+void ValidateRgn( HWND h, HRGN r ) {
+ HRGN invalid = (HRGN)GetWindowLong( h, GWL_INVALIDREGION );
+ if ( ! invalid ) return;
+
+ ASSERT( r != invalid );
+ XSubtractRegion( invalid, r, invalid );
+}
+#endif // wnd
+
+int SubtractRect( RECT *out, RECT *in1, RECT *in2 ) {
+ int ret;
+ if ( in1->left >= in2->left && in1->right <= in2->right ) {
+ out->left = in1->left; out->right = in1->right;
+
+ if ( in1->top >= in2->top && in2->bottom >= in2->top && in2->bottom <= in2->bottom ) {
+ out->top = in1->bottom; out->bottom = in2->bottom;
+
+ ret = 1;
+ } else if ( in1->top <= in2->top && in1->bottom >= in2->top && in1->bottom <= in2->bottom ) {
+ out->top = in1->top; out->bottom = in2->top;
+
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+
+ } else if ( in1->top >= in2->top && in1->bottom <= in2->bottom ) {
+ out->top = in1->top; out->bottom = in1->bottom;
+
+ if ( in1->left >= in2->left && in2->right >= in2->left && in2->right <= in2->right ) {
+ out->left = in1->right; out->right = in2->right;
+
+ ret = 1;
+ } else if ( in1->left <= in2->left && in1->right >= in2->left && in1->right <= in2->right ) {
+ out->left = in1->left; out->right = in2->left;
+
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+
+
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+int EqualRect( RECT *a, RECT *b ) {
+ return ( a->top == b->top && a->bottom == b->bottom &&
+ a->left == b->left && a->right == b->right );
+}
+
+#ifdef WASABI_COMPILE_WND
+
+HWND WindowFromPoint( POINT p ) {
+ int x, y;
+ Window child;
+
+ XTranslateCoordinates( Linux::getDisplay(), Linux::RootWin(), Linux::RootWin(), p.x, p.y, &x, &y, &child );
+ return child;
+}
+#endif // wnd
+
+void CopyFile( const char *f1, const char *f2, BOOL b ) {
+ COPYFILE( f1, f2 );
+}
+
+DWORD GetModuleFileName(void *pid, const char *filename, int bufsize) {
+ char procbuffer[512];
+ sprintf(procbuffer, "/proc/%d/exe", (int)pid);
+ return readlink(procbuffer, (char *)filename, bufsize);
+}
+
+const char *CharPrev(const char *lpszStart, const char *lpszCurrent) {
+ if (lpszCurrent-1 >= lpszStart) return lpszCurrent-1;
+ return lpszStart;
+}
+
+#endif