From 27d1e03d7bdf8fcfe7292c06e40bc3e2fca9158e Mon Sep 17 00:00:00 2001 From: Denis Oliver Kropp Date: Tue, 19 Oct 2010 15:56:15 +0200 Subject: pluggit --- src/SourceX11.cxx | 534 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 src/SourceX11.cxx (limited to 'src/SourceX11.cxx') diff --git a/src/SourceX11.cxx b/src/SourceX11.cxx new file mode 100644 index 0000000..339c8e7 --- /dev/null +++ b/src/SourceX11.cxx @@ -0,0 +1,534 @@ +extern "C" { +#include +} + +namespace PluggIt { + +D_DEBUG_DOMAIN( PluggIt_SourceX11, "PluggIt/SourceX11", "PluggIt Source X11" ); + + +class SourceX11 extends Source, Runnable +{ + private Display *m_display; + private Screen *m_screen; + private Window m_window; + private Window m_root; + + private DFBPoint m_position; + private DFBDimension m_size; + private unsigned int m_depth; + + private XImage *m_image; + private Pixmap m_pixmap; + + private Bool m_shm; + private int m_shm_major; + private int m_shm_minor; + private XShmSegmentInfo m_shm_info; + + private Damage m_xdamage; + private int m_xdamage_notify_event; + private XserverRegion m_xdamage_region; + private GC m_xdamage_copy_gc; + + private Thread *m_thread; + + private Updates m_updates; + + + + public class Config extends Source::Config { + friend class SourceX11; + + public typedef enum { + WINDOW_SELECTION_NONE, + WINDOW_SELECTION_TITLE, + WINDOW_SELECTION_INTERACTIVE + } WindowSelection; + + private WindowSelection m_selection; + private std::string m_title; + + + public Config( WindowSelection selection, const std::string &title = std::string() ) { + m_selection = selection; + m_title = title; + } + }; + + + public SourceX11( View *view, const Config &config ) : m_updates(16), Source( view, config ) { + D_DEBUG_AT( PluggIt_SourceX11, "%s( %p )\n", __FUNCTION__, view ); + + XInitThreads(); + + m_display = XOpenDisplay( getenv("DISPLAY") ); + if (!m_display) + throw new Exception( "Error in XOpenDisplay for '%s'!\n", getenv("DISPLAY") ); + + XLockDisplay( m_display ); + + m_screen = DefaultScreenOfDisplay( m_display ); + m_root = RootWindowOfScreen( m_screen ); + + switch (config.m_selection) { + case Config::WINDOW_SELECTION_NONE: + m_window = m_root; + break; + + case Config::WINDOW_SELECTION_TITLE: + m_window = findWindow( config.m_title ); + if (m_window == None) + throw new Exception( "Could not find window with title containing '%s'\n", config.m_title.c_str() ); + break; + + case Config::WINDOW_SELECTION_INTERACTIVE: + m_window = pickWindow(); + if (m_window == None) + throw new Exception( "Could not interactively pick a window\n" ); + break; + + default: + throw new Exception( "Invalid window selection method\n" ); + } + + unsigned int tmp_u; + int tmp_i; + + XGetGeometry( m_display, m_window, &m_root, &m_position.x, &m_position.y, + (unsigned int*) &m_size.w, (unsigned int*) &m_size.h, &tmp_u, &m_depth ); + + + std::string t = getWindowTitle( m_window ); + + D_INFO( "Source/X11: Window %d,%d-%ux%u, depth %u, name '%s'\n", + m_position.x, m_position.y, m_size.w, m_size.h, m_depth, t.c_str() ); + + + if (XShmQueryExtension( m_display )) + XShmQueryVersion( m_display, &m_shm_major, &m_shm_minor, &m_shm ); + + initXDamage(); + + if (m_shm) { + if (!initXShmImage()) { + m_shm = false; + + initXImage(); + } + } + else + initXImage(); + + XUnlockDisplay( m_display ); + + + m_view->config( m_size ); + + + /* Create the thread object. */ + m_thread = new Thread( this, "SourceX11" ); + + m_thread->start(); + } + + virtual ~SourceX11() { + XDestroyImage( m_image ); + + if (m_shm) { + XShmDetach( m_display, &m_shm_info ); + + shmdt( m_shm_info.shmaddr ); + } + } + + +/**********************************************************************************************************************/ + + private void run() { + D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ ); + + XLockDisplay( m_display ); + + while (true) { + XEvent event; + + XNextEvent( m_display, &event ); + + if (event.type == m_xdamage_notify_event) + handleXDamage( reinterpret_cast( &event ) ); + + if (!XPending( m_display )) + flushXDamage(); + } + + XUnlockDisplay( m_display ); + } + + +/**********************************************************************************************************************/ + + private void initXDamage() { + int event_base, error_base; + int major, minor; + XGCValues values; + + D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ ); + + if (!XDamageQueryExtension( m_display, &event_base, &error_base )) + throw new Exception( "XDamageQueryExtension" ); + + if (!XDamageQueryVersion( m_display, &major, &minor ) || major != 1) + throw new Exception( "XDamageQueryVersion" ); + + m_xdamage_notify_event = event_base + XDamageNotify; + + m_xdamage = XDamageCreate( m_display, m_window, XDamageReportDeltaRectangles ); + if (m_xdamage == None) + throw new Exception( "XDamageCreate" ); + + m_xdamage_region = XFixesCreateRegion( m_display, NULL, 0 ); + if (m_xdamage_region == None) { + XDamageDestroy( m_display, m_xdamage ); + m_xdamage = None; + throw new Exception( "XFixesCreateRegion" ); + } + + values.subwindow_mode = IncludeInferiors; + + m_xdamage_copy_gc = XCreateGC( m_display, m_window, GCSubwindowMode, &values ); + } + + private void handleXDamage( const XDamageNotifyEvent *event ) { + D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ ); + + D_DEBUG_AT( PluggIt_SourceX11, " -> %4d,%4d-%4dx%4d\n", + event->area.x, event->area.y, event->area.width, event->area.height ); + + + DFBRegion damage( event->area.x, event->area.y, + event->area.x + event->area.width - 1, + event->area.y + event->area.height - 1 ); + + m_updates.addRegion( damage ); + } + + private void flushXDamage() { + vector rects; + + D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ ); + + m_updates.GetRectangles( rects ); + + if (rects.empty()) + return; + + const DFBRegion &bounding = m_updates.bounding(); + + D_DEBUG_AT( PluggIt_SourceX11, " -> %4d,%4d-%4dx%4d (%d regions, %d rectangles)\n", + DFB_RECTANGLE_VALS_FROM_REGION( &bounding ), m_updates.num_regions(), rects.size() ); + + m_updates.reset(); + + + XRectangle xdamage[rects.size()]; + + for (int i=0; i [%2d] %4d,%4d-%4dx%4d\n", i, DFB_RECTANGLE_VALS( &rects[i] ) ); + + xdamage[i].x = rects[i].x; + xdamage[i].y = rects[i].y; + xdamage[i].width = rects[i].w; + xdamage[i].height = rects[i].h; + } + + XFixesSetRegion( m_display, m_xdamage_region, xdamage, rects.size() ); + XDamageSubtract( m_display, m_xdamage, m_xdamage_region, None ); + + + if (m_shm) { + for (int i=0; i [%2d] %4d,%4d-%4dx%4d\n", i, DFB_RECTANGLE_VALS( &rects[i] ) ); + } + + m_view->update( rects, m_image->data, m_image->bytes_per_line ); + } + +/**********************************************************************************************************************/ + + private bool initXShmImage() { + m_image = XShmCreateImage( m_display, + DefaultVisual( m_display, 0 ), + m_depth, + ZPixmap, + NULL, + &m_shm_info, + m_size.w, + m_size.h ); + if (!m_image) + return false; + + m_shm_info.shmid = shmget( IPC_PRIVATE, m_image->bytes_per_line * m_image->height, IPC_CREAT | 0600 ); + if (m_shm_info.shmid == -1) { + XDestroyImage( m_image ); + return false; + } + + m_shm_info.readOnly = False; + m_shm_info.shmaddr = (char*) shmat( m_shm_info.shmid, 0, 0 ); + + if (m_shm_info.shmaddr == (char*) -1) { + shmctl( m_shm_info.shmid, IPC_RMID, 0 ); + XDestroyImage( m_image ); + return false; + } + + m_image->data = m_shm_info.shmaddr; + + + XShmAttach( m_display, &m_shm_info ); + XSync( m_display, False ); + + shmctl( m_shm_info.shmid, IPC_RMID, 0 ); + + + m_pixmap = XShmCreatePixmap( m_display, m_window, m_image->data, &m_shm_info, + m_image->width, m_image->height, m_image->depth ); + if (!m_pixmap) { + shmdt( m_shm_info.shmaddr ); + XShmDetach( m_display, &m_shm_info ); + XDestroyImage( m_image ); + return false; + } + + return true; + } + + private void initXImage() { + m_image = XCreateImage( m_display, + DefaultVisual( m_display, 0 ), + m_depth, + ZPixmap, + 0, + NULL, + m_size.w, + m_size.h, + 32, + 0 ); + if (!m_image) + throw new Exception( "XCreateImage" ); + + m_image->data = (char*) malloc( m_image->bytes_per_line * m_image->height ); + if (!m_image->data) { + XDestroyImage( m_image ); + throw new OutOfMemoryException(); + } + } + + +/**********************************************************************************************************************/ + + private std::string getWindowTitle( Window window ) { + std::string str; + char *name = NULL; + Atom actual_type; + int format; + unsigned long n; + unsigned long extra; + + XGetWindowProperty( m_display, window, XA_WM_NAME, 0L, 100L, + False, AnyPropertyType, &actual_type, &format, &n, + &extra, (unsigned char **) &name ); + + D_DEBUG_AT( PluggIt_SourceX11, "%s( %u ) -> '%s'\n", __FUNCTION__, window, name ); + + if (name) { + str.append( name ); + + XFree( name ); + } + + return str; + } + + private Window checkWindow( Window window, + const std::string &title ) + { + std::string t; + Window w = None, root, parent, *children; + unsigned int num_children; + + D_DEBUG_AT( PluggIt_SourceX11, "%s( %u, %s )\n", __FUNCTION__, window, title.c_str() ); + + t = getWindowTitle( window ); + if (t == title || t.find( title ) < t.size()) + return window; + + XQueryTree( m_display, window, &root, &parent, &children, &num_children ); + + for (unsigned int i=0; i not found\n" ); + + return None; + } + + +/**********************************************************************************************************************/ + + private Window pickWindow() { + int loops; + Window win = None; + + D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ ); + + D_DEBUG_AT( PluggIt_SourceX11, " -> Grabbing pointer...\n" ); + + XGrabPointer( m_display, m_root, False, ButtonPressMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, None, XCreateFontCursor( m_display, XC_cross ), CurrentTime ); + + for (loops=0; loops < 100 && win == None; loops++) { + XEvent event; + + D_DEBUG_AT( PluggIt_SourceX11, " -> Waiting for event...\n" ); + + if (!XPending( m_display )) { + usleep( 40000 ); + continue; + } + + XNextEvent( m_display, &event ); + + D_DEBUG_AT( PluggIt_SourceX11, " -> event type %d\n", event.type ); + + switch (event.type) { + case ButtonPress: + D_DEBUG_AT( PluggIt_SourceX11, " -> button %d press at %d,%d\n", + event.xbutton.button, event.xbutton.x, event.xbutton.y ); + + D_DEBUG_AT( PluggIt_SourceX11, " -> sub window %u: '%s'\n", + event.xbutton.subwindow, getWindowTitle( event.xbutton.subwindow ).c_str() ); + + win = lookupManagedWindow( event.xbutton.subwindow ); + break; + + default: + break; + } + + loops = 0; + } + + XUngrabPointer( m_display, CurrentTime ); + + return win; + } + + +/**********************************************************************************************************************/ + + private Window lookupManagedWindow( Window window ) { + int i, result; + unsigned long nitems; + unsigned long bytes_after; + unsigned long *prop; + Atom ret_type = None; + int ret_format; + Atom wm_state_atom; + + D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ ); + + wm_state_atom = XInternAtom( m_display, "WM_STATE", False ); + + + D_DEBUG_AT( PluggIt_SourceX11, " -> Retrieving WM_STATE (Atom %d)...\n", wm_state_atom ); + + result = XGetWindowProperty( m_display, window, wm_state_atom, + 0, LONG_MAX, False, wm_state_atom, + &ret_type, &ret_format, &nitems, + &bytes_after, (unsigned char**) &prop ); + if (result == Success) { + XFree( prop ); + + if (ret_type == wm_state_atom) { + D_DEBUG_AT( PluggIt_SourceX11, " -> OK\n" ); + return window; + } + } + + + + Window root; + Window parent; + Window *children = NULL; + Window win = None; + unsigned int num_children; + + result = XQueryTree( m_display, window, &root, &parent, &children, &num_children ); + if (!result) + return None; + + for (i=0; i