diff options
author | Jon TURNEY <jon.turney@dronecode.org.uk> | 2012-11-21 13:22:25 +0000 |
---|---|---|
committer | Jon TURNEY <jon.turney@dronecode.org.uk> | 2012-11-30 14:26:55 +0000 |
commit | 801f043572eab2507f752b13fd504dcb01060bdd (patch) | |
tree | 1f9891af7eae92c72d21a1d38ac160af58d0f922 | |
parent | 0708bdfb75122e788b891bb100df428f8e396c9c (diff) |
Add shaped window support
-rw-r--r-- | src/main.c | 4 | ||||
-rw-r--r-- | src/wndproc.c | 118 | ||||
-rw-r--r-- | src/wndproc.h | 1 |
3 files changed, 123 insertions, 0 deletions
@@ -81,6 +81,10 @@ eventHandler(const xcwm_event_t *event) case XCWM_EVENT_WINDOW_APPEARANCE: UpdateStyle(window); break; + + case XCWM_EVENT_WINDOW_SHAPE: + UpdateShape(window); + break; } } diff --git a/src/wndproc.c b/src/wndproc.c index 197d659..ea3cd4e 100644 --- a/src/wndproc.c +++ b/src/wndproc.c @@ -663,6 +663,123 @@ winMousePollingTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) } } +/* + * Updates the shape of a HWND according to its X SHAPE bounding region +*/ +void +UpdateShape(xcwm_window_t *window) +{ + HRGN hRgn = NULL; + + HWND hWnd = xcwm_window_get_local_data(window); + if (!hWnd) + return; + + xcb_rectangle_iterator_t ri = xcwm_window_get_shape(window); + DEBUG("UpdateShape: %d rects\n", ri.rem); + + /* If window isn't shaped, we can just use a NUL region, otherwise... */ + if (ri.rem > 0) + { + HRGN hRgnRect; + RECT rcClient; + RECT rcWindow; + int iOffsetX, iOffsetY; + + /* Windows region is in window, not client area, coordinates */ + + /* Get client rectangle */ + if (!GetClientRect(hWnd, &rcClient)) { + fprintf(stderr, "UpdateShape - GetClientRect failed, bailing: %d\n", + (int)GetLastError()); + return; + } + + /* Translate client rectangle coords to screen coords */ + /* NOTE: Only transforms top and left members */ + ClientToScreen(hWnd, (LPPOINT) &rcClient); + + /* Get window rectangle */ + if (!GetWindowRect(hWnd, &rcWindow)) { + fprintf(stderr, "UpdateShape - GetWindowRect failed, bailing: %d\n", + (int)GetLastError()); + return; + } + + /* Calculate offset from window upper-left to client upper-left */ + iOffsetX = rcClient.left - rcWindow.left; + iOffsetY = rcClient.top - rcWindow.top; + DEBUG("UpdateShape: offset to client area from window origin is (%d,%d)\n", iOffsetX, iOffsetY); + + /* Start with an empty region */ + hRgn = CreateRectRgn(0, 0, 0, 0); + if (hRgn == NULL) { + fprintf(stderr, "UpdateShape - Initial CreateRectRgn failed: %d\n", + (int)GetLastError()); + } + + /* Loop through all rectangles in the X region */ + for (; ri.rem; xcb_rectangle_next(&ri)) + { + /* DEBUG("UpdateShape: rect %d @ (%d,%d) %dx%d\n", ri.index, */ + /* ri.data->x, ri.data->y, ri.data->width, ri.data->height); */ + + /* Create a Windows region for the X rectangle */ + hRgnRect = CreateRectRgn(ri.data->x + iOffsetX, + ri.data->y + iOffsetY, + ri.data->x + iOffsetX + ri.data->width, + ri.data->y + iOffsetY + ri.data->height); + if (hRgnRect == NULL) { + fprintf(stderr, "UpdateShape - CreateRectRgn failed: %d\n", + (int) GetLastError()); + } + + /* Merge the Windows region with the accumulated region */ + if (CombineRgn(hRgn, hRgn, hRgnRect, RGN_OR) == ERROR) { + fprintf(stderr, "UpdateShape - CombineRgn () failed: %d\n", + (int) GetLastError()); + } + + /* Delete the temporary Windows region */ + DeleteObject(hRgnRect); + } + + /* + Since the X SHAPE client bounding region may extend outside the window, and is defined to be + clipped to X windows bounding region, we need to take the intersection of our region with the client + area, to ensure we get consistent appearance, otherwise the native frame may be drawn or not + if the X SHAPE client bounding region extends outside the X windows bounding region. + */ + hRgnRect = CreateRectRgn(iOffsetX, iOffsetY, iOffsetX + rcClient.right, iOffsetY + rcClient.bottom); + if (hRgnRect == NULL) { + fprintf(stderr, "UpdateShape - Titlebar CreateRectRgn failed: %d\n", + (int)GetLastError()); + } + if (CombineRgn(hRgn, hRgn, hRgnRect, RGN_AND) == ERROR) { + fprintf(stderr, "UpdateShape - CombineRgn () failed: %d\n", + (int) GetLastError()); + } + DeleteObject(hRgnRect); + + /* Now add title bar area, so that it get's drawn if window is framed */ + /* FIXME: Mean, nasty, ugly hack!!! */ + hRgnRect = CreateRectRgn(0, 0, rcWindow.right, iOffsetY); + if (hRgnRect == NULL) { + fprintf(stderr, "UpdateShape - Titlebar CreateRectRgn failed: %d\n", + (int)GetLastError()); + } + if (CombineRgn(hRgn, hRgn, hRgnRect, RGN_OR) == ERROR) { + fprintf(stderr, "UpdateShape - CombineRgn () failed: %d\n", + (int) GetLastError()); + } + DeleteObject(hRgnRect); + } + + SetWindowRgn(hWnd, hRgn, TRUE); + /* The system now owns the region specified by the region handle and will delete + it when it is no longer needed. */ +} + static void winStartMousePolling(void) { @@ -1318,6 +1435,7 @@ winCreateWindowsWindow(xcwm_window_t *window) UpdateName(window); /* UpdateIcon(); */ UpdateStyle(window); + UpdateShape(window); /* Display the window without activating it */ ShowWindow(hWnd, SW_SHOWNOACTIVATE); diff --git a/src/wndproc.h b/src/wndproc.h index 8c60d58..5b11f89 100644 --- a/src/wndproc.h +++ b/src/wndproc.h @@ -25,6 +25,7 @@ void UpdateName(xcwm_window_t *window); void UpdateImage(xcwm_window_t *window); void UpdateStyle(xcwm_window_t *window); +void UpdateShape(xcwm_window_t *window); void winCreateWindowsWindow(xcwm_window_t *window); void winDestroyWindowsWindow(xcwm_window_t *window); |