summaryrefslogtreecommitdiff
path: root/src/SourceWin32.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/SourceWin32.cxx')
-rw-r--r--src/SourceWin32.cxx653
1 files changed, 653 insertions, 0 deletions
diff --git a/src/SourceWin32.cxx b/src/SourceWin32.cxx
new file mode 100644
index 0000000..25ac45b
--- /dev/null
+++ b/src/SourceWin32.cxx
@@ -0,0 +1,653 @@
+namespace PluggIt {
+
+D_DEBUG_DOMAIN( PluggIt_SourceWin32, "PluggIt/SourceWin32", "PluggIt Source Win32" );
+
+
+#include "ScreenHooks.h"
+
+const UINT specIpcCode = RegisterWindowMessage("HOOK.MESSAGE.CODE");
+
+
+class SourceWin32 extends Source, Runnable
+{
+ private HINSTANCE m_hInstance;
+
+ private HWND m_root;
+ private HWND m_window;
+
+ private DFBPoint m_position;
+ private DFBDimension m_size;
+
+ private HBITMAP m_bitmap;
+ private void *m_pixels;
+ private int m_pitch;
+
+ private HBITMAP m_bitmap2;
+ private void *m_pixels2;
+ private int m_pitch2;
+
+ private HDC m_window_dc;
+ private HDC m_bitmap_dc;
+ private HDC m_bitmap_dc2;
+
+ private Thread *m_thread;
+
+ private Updates m_updates;
+
+ private VIDEODRIVER m_driver;
+ private bool m_using_driver;
+
+ private pthread_mutex_t m_lock;
+ private pthread_cond_t m_cond;
+
+
+ public class Config extends Source::Config {
+ friend class SourceWin32;
+
+ public typedef enum {
+ WINDOW_SELECTION_NONE,
+ WINDOW_SELECTION_TITLE,
+ WINDOW_SELECTION_INTERACTIVE
+ } WindowSelection;
+
+ private WindowSelection m_selection;
+ private std::string m_title;
+ private bool m_updating;
+ private bool m_use_driver;
+
+
+ public Config( WindowSelection selection,
+ const std::string &title = std::string(),
+ bool updating = false, bool use_driver = true )
+ {
+ m_selection = selection;
+ m_title = title;
+ m_updating = updating;
+ m_use_driver = use_driver;
+ }
+ };
+
+
+ public SourceWin32( View *view, const Config &config ) : Source( view, config ), m_updates(16) {
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s( %p )\n", __FUNCTION__, view );
+
+ m_hInstance = GetModuleHandle( NULL );
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> hInstance %p\n", m_hInstance );
+
+
+ m_root = GetDesktopWindow();
+
+ 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 == 0)
+ throw new Exception( "Could not find window with title containing '%s'\n", config.m_title.c_str() );
+ break;
+
+ case Config::WINDOW_SELECTION_INTERACTIVE:
+ pickWindow();
+ if (m_window == 0)
+ throw new Exception( "Could not interactively pick a window\n" );
+ break;
+
+ default:
+ throw new Exception( "Invalid window selection method\n" );
+ }
+
+
+ RECT rect;
+
+ GetClientRect( m_window, &rect );
+
+ m_position.x = rect.left;
+ m_position.y = rect.top;
+
+ m_size.w = rect.right - rect.left;
+ m_size.h = rect.bottom - rect.top;
+
+ queueUpdate( 0, 0, m_size.w - 1, m_size.h - 1 );
+
+
+ std::string t = getWindowTitle( m_window );
+
+ D_INFO( "Source/Win32: Window %d,%d-%ux%u, name '%s'\n",
+ m_position.x, m_position.y, m_size.w, m_size.h, t.c_str() );
+
+ if (config.m_use_driver)
+ m_driver.VIDEODRIVER_start( m_position.x, m_position.y, m_size.w, m_size.h );
+
+ m_using_driver = m_driver.mypchangebuf != NULL;
+
+ m_view->config( m_size );
+
+
+ m_window_dc = GetDC( m_window );
+ m_bitmap_dc = CreateCompatibleDC( m_window_dc );
+ m_bitmap_dc2 = CreateCompatibleDC( m_window_dc );
+
+
+ BITMAPINFO bitmap_info = {{
+ biSize: sizeof(BITMAPINFOHEADER),
+ biWidth: m_size.w,
+ biHeight: m_size.h,
+ biPlanes: 1,
+ biBitCount: 32,
+ biCompression: BI_RGB,
+ biSizeImage: 0,
+ biXPelsPerMeter: 0,
+ biYPelsPerMeter: 0,
+ biClrUsed: 0,
+ biClrImportant: 0,
+ }};
+
+ m_bitmap = CreateDIBSection( m_window_dc, &bitmap_info, DIB_RGB_COLORS, &m_pixels, NULL, 0 );
+ m_bitmap2 = CreateDIBSection( m_window_dc, &bitmap_info, DIB_RGB_COLORS, &m_pixels2, NULL, 0 );
+
+
+ BITMAP bitmap;
+
+ GetObject( m_bitmap, sizeof(BITMAP), &bitmap );
+
+ m_pitch = bitmap.bmWidthBytes;
+
+ GetObject( m_bitmap2, sizeof(BITMAP), &bitmap );
+
+ m_pitch2 = bitmap.bmWidthBytes;
+
+
+ SelectObject( m_bitmap_dc, m_bitmap );
+ SelectObject( m_bitmap_dc2, m_bitmap2 );
+
+
+ pthread_mutex_init( &m_lock, NULL );
+ pthread_cond_init( &m_cond, NULL );
+
+
+ if (!m_using_driver) {
+ HWND w;
+
+ WNDCLASSEX wndClass;
+
+ ZeroMemory( &wndClass, sizeof(wndClass) );
+
+ wndClass.cbSize = sizeof(wndClass);
+ wndClass.style = CS_HREDRAW | CS_VREDRAW;
+ wndClass.lpfnWndProc = MsgWndProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = GetModuleHandle(NULL);
+ wndClass.hIcon = NULL;
+ wndClass.hIconSm = NULL;
+ wndClass.hCursor = LoadCursor( NULL, IDC_UPARROW );
+ wndClass.hbrBackground = (HBRUSH) GetStockObject( NULL_BRUSH );
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = "MsgWindow";
+
+ if (!RegisterClassEx( &wndClass ))
+ throw new Exception( "RegisterClassEx() failed!" );
+
+
+ w = CreateWindowEx( 0,
+ "MsgWindow",
+ "MsgWindow",
+ WS_POPUP,// | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ 1,
+ 1,
+ NULL,
+ NULL,
+ GetModuleHandle(NULL),
+ this );
+
+ if (!setHook( w ))
+ throw new Exception( "Failed to set hooks!" );
+ }
+
+
+ /* Create the thread object. */
+ m_thread = new Thread( this, "SourceWin32" );
+
+ m_thread->start();
+ }
+
+ virtual ~SourceWin32() {
+ m_thread->interrupt();
+ m_thread->join();
+ delete m_thread;
+
+ pthread_mutex_destroy( &m_lock );
+ pthread_cond_destroy( &m_cond );
+
+ DeleteDC( m_window_dc );
+ DeleteDC( m_bitmap_dc );
+ DeleteDC( m_bitmap_dc2 );
+
+ DeleteObject( m_bitmap );
+ DeleteObject( m_bitmap2 );
+ }
+
+ private void addRect( const RECT &rect ) {
+ if (rect.left >= rect.right)
+ return;
+
+ if (rect.top >= rect.bottom)
+ return;
+
+ DFBRegion region( rect.left,
+ rect.top,
+ rect.right - 1,
+ rect.bottom - 1 );
+
+ m_updates.addRegion( region );
+ }
+
+ private void run() {
+ if (m_using_driver) {
+ D_ASSERT( m_driver.mypchangebuf != NULL );
+
+ unsigned int counter = m_driver.mypchangebuf->counter;
+ unsigned int i;
+
+ long long last_time = direct_clock_get_abs_millis();
+
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ );
+
+ while (!m_thread->isInterrupted()) {
+ if (m_driver.mypchangebuf->counter < 1 ||
+ m_driver.mypchangebuf->counter >= MAXCHANGES_BUF ||
+ counter == m_driver.mypchangebuf->counter)
+ usleep( 10000 );
+
+ if (counter != m_driver.mypchangebuf->counter) {
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> counter: %d -> %d\n", counter, m_driver.mypchangebuf->counter );
+
+ if (counter < m_driver.mypchangebuf->counter) {
+ for (i=counter+1; i<=m_driver.mypchangebuf->counter; i++) {
+ addRect( m_driver.mypchangebuf->pointrect[i].rect );
+ }
+ }
+ else if (counter > m_driver.mypchangebuf->counter) {
+ for (i=counter+1; i<MAXCHANGES_BUF; i++) {
+ addRect( m_driver.mypchangebuf->pointrect[i].rect );
+ }
+ for (i=1; i<=m_driver.mypchangebuf->counter; i++) {
+ addRect( m_driver.mypchangebuf->pointrect[i].rect );
+ }
+ }
+
+ counter = m_driver.mypchangebuf->counter;
+ }
+
+ if (direct_clock_get_abs_millis() - last_time > 2000) {
+ DFBRegion region( 0,
+ 0,
+ m_size.w - 1,
+ m_size.h - 1 );
+
+ m_updates.addRegion( region );
+
+ last_time = direct_clock_get_abs_millis();
+ }
+
+ if (m_updates.num_regions() > 0) {
+ vector<DFBRectangle_C> rects;
+
+ m_updates.GetRectangles( rects );
+
+
+ const DFBRegion &bounding = m_updates.bounding();
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> %4d,%4d-%4dx%4d (%d regions, %d rectangles)\n",
+ DFB_RECTANGLE_VALS_FROM_REGION( &bounding ), m_updates.num_regions(), rects.size() );
+
+ m_updates.reset();
+
+ flushUpdates( rects );
+ }
+ }
+ }
+ else {
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ );
+
+ long long last_time = direct_clock_get_abs_millis();
+
+ while (!m_thread->isInterrupted()) {
+ if (((const Config&)m_config).m_updating) {
+ usleep( 100000 );
+
+ queueUpdate( 0, 0, m_size.w - 1, m_size.h - 1 );
+
+ pthread_mutex_lock( &m_lock );
+ }
+ else {
+ pthread_mutex_lock( &m_lock );
+
+ if (m_updates.num_regions() == 0)
+ usleep( 10000 );
+ //pthread_cond_wait( &m_cond, &m_lock );
+ }
+
+ if (direct_clock_get_abs_millis() - last_time > 2000) {
+ DFBRegion region( 0,
+ 0,
+ m_size.w - 1,
+ m_size.h - 1 );
+
+ m_updates.addRegion( region );
+
+ last_time = direct_clock_get_abs_millis();
+ }
+
+
+ vector<DFBRectangle_C> rects;
+
+ if (m_updates.num_regions() > 0) {
+ m_updates.GetRectangles( rects );
+
+
+ const DFBRegion &bounding = m_updates.bounding();
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> %4d,%4d-%4dx%4d (%d regions, %d rectangles)\n",
+ DFB_RECTANGLE_VALS_FROM_REGION( &bounding ), m_updates.num_regions(), rects.size() );
+
+ m_updates.reset();
+ }
+
+ pthread_mutex_unlock( &m_lock );
+
+ if (!rects.empty())
+ flushUpdates( rects );
+ }
+ }
+ }
+
+/**********************************************************************************************************************/
+
+ private void queueUpdate( int x1, int y1, int x2, int y2 ) {
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s( %d,%d-%dx%d )\n", __FUNCTION__, x1, y1, x2 - x1 + 1, y2 - y1 + 1 );
+
+ pthread_mutex_lock( &m_lock );
+
+ DFBRegion damage( x1, y1, x2, y2 );
+
+ m_updates.addRegion( damage );
+
+ pthread_mutex_unlock( &m_lock );
+
+ pthread_cond_signal( &m_cond );
+ }
+
+ private bool flushUpdates( vector<DFBRectangle_C> &rects ) {
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ );
+
+ if (m_using_driver) {
+ m_view->update( rects, m_driver.myframebuffer, m_pitch );
+ }
+ else {
+ for (unsigned int i=0; i<rects.size(); i++) {
+ BitBlt( m_bitmap_dc,
+ rects[i].x,
+ rects[i].y,
+ rects[i].w,
+ rects[i].h,
+ m_window_dc,
+ rects[i].x,
+ rects[i].y,
+ SRCCOPY );
+ }
+
+ m_view->update( rects, (void*)((char*) m_pixels + m_pitch * (m_size.h - 1)), - m_pitch );
+ }
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> flush done\n" );
+
+ return true;
+ }
+
+
+
+/**********************************************************************************************************************/
+
+ private static std::string getWindowTitle( HWND window ) {
+ char buf[1024] = { 0 };
+
+ GetWindowText( window, buf, sizeof(buf) );
+
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s( %p ) -> '%s'\n", __FUNCTION__, window, buf );
+
+ return buf;
+ }
+
+
+/**********************************************************************************************************************/
+
+ class EnumContext {
+ public const std::string &title;
+ public HWND window;
+
+ public EnumContext( const std::string &title ) : title(title), window(0) {}
+ };
+
+ private static BOOL CALLBACK enumWindowsProc( HWND hwnd,
+ LPARAM lParam )
+ {
+ EnumContext *ctx = reinterpret_cast<EnumContext *>( lParam );
+
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s( %p )\n", __FUNCTION__, hwnd );
+
+ std::string title = getWindowTitle( hwnd );
+
+ if (title == ctx->title || title.find( ctx->title ) < title.size())
+ ctx->window = hwnd;
+
+ return ctx->window == NULL;
+ }
+
+ private HWND findWindow( const std::string &title ) {
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s( %s )\n", __FUNCTION__, title.c_str() );
+
+ EnumContext ctx( title );
+
+ EnumWindows( enumWindowsProc, reinterpret_cast<LPARAM>( &ctx ) );
+
+ return ctx.window;
+ }
+
+
+/**********************************************************************************************************************/
+
+ static LRESULT CALLBACK WndProc( HWND hwnd, // handle to window
+ UINT uMsg, // message identifier
+ WPARAM wParam, // first message parameter
+ LPARAM lParam ) // second message parameter
+ {
+ static SourceWin32 *thiz;
+ POINT point;
+ CREATESTRUCT *create;
+
+ switch (uMsg) {
+ case WM_CREATE:
+ create = reinterpret_cast<LPCREATESTRUCT>( lParam );
+ thiz = reinterpret_cast<SourceWin32 *>( create->lpCreateParams );
+
+ SetCursor( LoadCursor( NULL, IDC_CROSS ) );
+ break;
+
+ case WM_LBUTTONDOWN:
+ ShowWindow( hwnd, SW_HIDE );
+
+
+ GetCursorPos( &point );
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> Cursor position is %ld,%ld\n", point.x, point.y );
+
+
+ thiz->m_window = WindowFromPoint( point );
+ if (thiz->m_window) {
+ HWND parent;
+
+ while ((parent = GetParent( thiz->m_window )) != NULL)
+ thiz->m_window = parent;
+ }
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> Window at cursor is %p: %s\n",
+ thiz->m_window, getWindowTitle( thiz->m_window ).c_str() );
+
+ DestroyWindow( hwnd );
+ break;
+
+ case WM_TIMER:
+ if (wParam == 666)
+ DestroyWindow( hwnd );
+ break;
+
+ case WM_DESTROY:
+ KillTimer( hwnd, 666 );
+ PostQuitMessage( 0 );
+ break;
+
+ default:
+ SetTimer( hwnd, 666, 5000, NULL );
+
+ return DefWindowProc( hwnd, uMsg, wParam, lParam );
+ }
+
+ return 0;
+ }
+
+ private void pickWindow() {
+ D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ );
+
+ HDC hdc;
+
+ hdc = GetDC( NULL );
+ if (!hdc)
+ throw new Exception( "GetDC() returned NULL" );
+
+ RECT rect;
+
+ GetClipBox( hdc, &rect );
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> Desktop size: %ldx%ld\n",
+ rect.right - rect.left, rect.bottom - rect.top );
+
+ ReleaseDC( NULL, hdc );
+
+
+
+ WNDCLASSEX wndClass;
+
+ ZeroMemory( &wndClass, sizeof(wndClass) );
+
+ wndClass.cbSize = sizeof(wndClass);
+ wndClass.style = CS_HREDRAW | CS_VREDRAW;
+ wndClass.lpfnWndProc = WndProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = m_hInstance;
+ wndClass.hIcon = NULL;
+ wndClass.hIconSm = NULL;
+ wndClass.hCursor = LoadCursor( NULL, IDC_UPARROW );
+ wndClass.hbrBackground = (HBRUSH) GetStockObject( NULL_BRUSH );
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = "GrabWindow";
+
+ if (!RegisterClassEx( &wndClass ))
+ throw new Exception( "RegisterClassEx() failed!\n" );
+
+
+ AdjustWindowRect( &rect, WS_CAPTION, FALSE );
+
+
+ HWND grabwin;
+
+ grabwin = CreateWindowEx( 0,
+ "GrabWindow",
+ "GrabWindow",
+ WS_POPUP,// | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ rect.right - rect.left,
+ rect.bottom - rect.top,
+ NULL,
+ NULL,
+ m_hInstance,
+ this );
+
+ SetWindowLong( grabwin, GWL_EXSTYLE,
+ GetWindowLong( grabwin, GWL_EXSTYLE ) | WS_EX_TRANSPARENT | WS_EX_TOPMOST );
+
+ ShowWindow( grabwin, SW_SHOW );
+
+
+
+ MSG msg;
+
+ while (GetMessage( &msg, 0, 0, 0 )) {
+ TranslateMessage( &msg );
+
+ D_DEBUG_AT( PluggIt_SourceWin32, " -> Dispatching event (id %u)...\n", msg.message );
+
+ DispatchMessage( &msg );
+ }
+ }
+
+/**********************************************************************************************************************/
+
+ static LRESULT CALLBACK MsgWndProc( HWND hwnd, // handle to window
+ UINT uMsg, // message identifier
+ WPARAM wParam, // first message parameter
+ LPARAM lParam ) // second message parameter
+ {
+ static SourceWin32 *thiz;
+ CREATESTRUCT *create;
+
+ switch (uMsg) {
+ case WM_CREATE:
+ create = reinterpret_cast<LPCREATESTRUCT>( lParam );
+ thiz = reinterpret_cast<SourceWin32 *>( create->lpCreateParams );
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage( 0 );
+ break;
+
+ default:
+ if (uMsg == specIpcCode) {
+ int x1 = wParam >> 16;
+ int y1 = wParam & 0xffff;
+ int x2 = lParam >> 16;
+ int y2 = lParam & 0xffff;
+
+ if (x1 < x2 && y1 < y2)
+ thiz->queueUpdate( x1, y1, x2 - 1, y2 - 1 );
+
+ break;
+ }
+
+ return DefWindowProc( hwnd, uMsg, wParam, lParam );
+ }
+
+ return 0;
+ }
+
+ public virtual int MainLoop() {
+ MSG msg;
+
+ while (GetMessage( &msg, 0, 0, 0 )) {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+
+ return 0;
+ }
+};
+
+}
+