Last active
February 11, 2020 10:43
-
-
Save lighth7015/488a88ded5233bf528038304c5057081 to your computer and use it in GitHub Desktop.
FindWindow()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#include <inttypes.h> | |
#include <xcb/xcb.h> | |
const char hPropName[] = "HWND"; | |
xcb_connection_t *connection; | |
xcb_screen_t *display; | |
xcb_atom_t | |
WindowCloseEvent, | |
WM; | |
typedef struct property { | |
const char* propName; | |
intptr_t* value; | |
} property_t, Property; | |
static void* nullptr = NULL; | |
static const char* ConnectionErrors[] = { | |
(const char *) 0, /* Error 0 */ | |
"Unable to connect to the X server or I/O error", /* XCB_CONN_ERROR */ | |
"Unsupported extension used", /* XCB_CONN_CLOSED_EXT_NOTSUPPORTED */ | |
"Out of memory", /* XCB_CONN_CLOSED_MEM_INSUFFICIENT */ | |
"Maximum allowed request length exceeded", /* XCB_CONN_CLOSED_REQ_LEN_EXCEED */ | |
"Failed to parse display string", /* XCB_CONN_CLOSED_PARSE_ERR */ | |
"No such screen on display", /* XCB_CONN_CLOSED_INVALID_SCREEN */ | |
"Error receiving connection FD" /* XCB_CONN_CLOSED_FDPASSING_FAILED */ | |
}; | |
struct Window; | |
typedef struct HWND { | |
struct Window *window; | |
} *HWND, *HMENU; | |
typedef struct Window { | |
xcb_drawable_t hWnd; | |
xcb_gcontext_t context; | |
const char *caption; | |
xcb_rectangle_t rect; | |
uint32_t nChildren; | |
HWND *children; | |
} *Window, window_t; | |
typedef uint32_t DWORD; | |
typedef unsigned long HINSTANCE; | |
typedef const char* LPCTSTR; | |
typedef void* LPVOID; | |
size_t hexdump(const char *title, const void *buffer, uint32_t size) { | |
uint32_t offset, written; | |
puts(""); | |
printf("*%*s %-66s *\n", 8, "", title); | |
for( offset = 0, written = 0 | |
; offset < size | |
; offset += 16) | |
{ | |
const uint8_t *address = (const uint8_t *) buffer + offset; | |
static char line[84] = { ' ' }; | |
int num_empty = 0, | |
length = (size - offset > 16) ? 16 : size - offset; | |
int empty = length == 16 && | |
!memcmp(address, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); | |
if ((num_empty = empty? num_empty + 1: 0) < 2) { | |
uint32_t written = sprintf( line, "%08" PRIx32 " ", offset), index, size; | |
line[written] = ' '; | |
for (index = 0; index < length; index++) { | |
written += sprintf(line + written, "%*s %02x", index == 8, "", address[index]); | |
line[written] = ' '; | |
} | |
size = 51 - 3 * length - (length >= 9); | |
written += sprintf( line + written, "%*s|", size, ""); | |
for (index = 0; index < length; index++) { | |
char ch = address[index] >= ' ' && address[index] <= '~'? address[index] : '.'; | |
written += sprintf( line + written, "%c", ch); | |
} | |
written += sprintf( line + written, "|"); | |
} | |
else if (num_empty == 2) { | |
sprintf( line, "*" ); | |
} | |
puts(line); | |
} | |
puts(""); | |
return written; | |
} | |
Property CreateProperty(const char* name) { | |
Property property = { .propName = name, .value = nullptr }; | |
return property; | |
} | |
Property CreatePropertyFrom(const char* name, void* address) { | |
Property property = CreateProperty(name); | |
property.value = address; | |
return property; | |
} | |
const char* | |
XcbQueryServerError( uint32_t* code ) { | |
*code = xcb_connection_has_error(connection); | |
return ConnectionErrors[*code]; | |
} | |
static inline xcb_atom_t | |
QueryAtom( const char* xcbAtomName ) { | |
xcb_generic_error_t *response; | |
xcb_intern_atom_reply_t* xcbResponse; | |
xcb_intern_atom_cookie_t xcbRequest = | |
xcb_intern_atom(connection, 1, strlen(xcbAtomName), xcbAtomName ); | |
if (( xcbResponse = xcb_intern_atom_reply( connection, xcbRequest, &response )) == XCB_ATOM_NONE) { | |
uint32_t status; | |
const char* message; | |
if ((message = XcbQueryServerError( &status ))) { | |
printf("Connection Error %d [%s].\n", status, message ); | |
} | |
else { | |
printf("Server replied [%s (%x, %x): No such atom.]\n", xcbAtomName, (uint32_t) xcbResponse, (uint32_t) response); | |
} | |
return XCB_ATOM_NONE; | |
} | |
return xcbResponse->atom; | |
} | |
static inline | |
bool XcbAtomCreate( Window hWnd, | |
const char* xcbAtomName, | |
xcb_atom_t xcbAtomType, | |
uint8_t xcbAtomBitLen, | |
uint32_t xcbAtomLength, | |
void* data) | |
{ | |
uint32_t status; | |
const char* message; | |
if ((message = XcbQueryServerError( &status )) == nullptr) { | |
xcb_atom_t xcbAtom = QueryAtom( xcbAtomName ); | |
xcb_generic_error_t *response; | |
if ( xcbAtom == XCB_ATOM_NONE) { | |
xcb_intern_atom_cookie_t xcbRequest = | |
xcb_intern_atom(connection, 0, strlen(xcbAtomName), xcbAtomName ); | |
xcb_intern_atom_reply_t* xcbResponse = | |
xcb_intern_atom_reply( connection, xcbRequest, &response ); | |
xcbAtom = xcbResponse->atom; | |
} | |
if (strcmp(xcbAtomName, hPropName) == 0) { | |
printf("%-16s: HWND address is %" PRIx64 ".\n", __FUNCTION__, (intptr_t) data ); | |
} | |
xcb_change_property(connection, | |
XCB_PROP_MODE_REPLACE, | |
hWnd->hWnd, | |
xcbAtom, | |
xcbAtomType, | |
xcbAtomBitLen, | |
xcbAtomLength, | |
data); | |
return true; | |
} | |
else { | |
printf("Connection Error %d [%s].\n", status, message ); | |
} | |
return false; | |
} | |
bool WindowPropertyGet( xcb_window_t hWnd, xcb_atom_t propertyType, const char* propertyName, intptr_t* value ) { | |
uint32_t status; | |
const char *response; | |
bool successful = false; | |
xcb_atom_t propName; | |
if (( response = XcbQueryServerError( &status )) == nullptr | |
&& (( propName = QueryAtom( propertyName )))) { | |
xcb_get_property_cookie_t request = | |
xcb_get_property(connection, 0, hWnd, propName, propertyType, 0, 0 ); | |
xcb_get_property_reply_t* handle = nullptr; | |
if (( handle = xcb_get_property_reply(connection, request, nullptr))) { | |
size_t length = 0; | |
intptr_t* address = (intptr_t *) xcb_get_property_value( handle ); | |
switch (propertyType) { | |
case XCB_ATOM_STRING: { | |
if (( length = xcb_get_property_value_length(handle)) > 0) { | |
void* buffer = *((void **) value) = (intptr_t *) calloc( 1, length ); | |
memcpy( buffer, xcb_get_property_value( handle ), length ); | |
} | |
else { | |
printf("%zu, %p\n", length, address); | |
} | |
} break; | |
default: | |
*((intptr_t **) value) = address; | |
break; | |
} | |
successful = true; | |
free( handle ); | |
} | |
else { | |
printf( "%s\n", response ); | |
} | |
} | |
else { | |
printf("Connection Error %d [%s].\n", status, response ); | |
} | |
return successful; | |
} | |
HWND FindWindow(xcb_window_t hWnd) { | |
HWND handle = 0; | |
uint32_t status; | |
const char *response; | |
bool successful = false; | |
xcb_atom_t propName; | |
if (( response = XcbQueryServerError( &status )) == nullptr | |
&& (( propName = QueryAtom( hPropName )))) { | |
uint32_t length = sizeof( intptr_t * ) / sizeof( uint32_t ) + 1; | |
xcb_get_property_cookie_t request = | |
xcb_get_property(connection, 0, hWnd, propName, XCB_ATOM_INTEGER, 0, length); | |
xcb_get_property_reply_t* handle = nullptr; | |
if (( handle = xcb_get_property_reply(connection, request, nullptr))) { | |
if (handle->type != XCB_NONE) { | |
HWND window = (HWND) *((intptr_t *) xcb_get_property_value( handle )); | |
//free( handle ); | |
//intptr_t ptrAddress = (intptr_t) (HWND) address; | |
printf("%-16s: HWND[%" PRIx64 "] is at address %lx\n", __FUNCTION__, (intptr_t) hWnd, (intptr_t) window ); | |
//hexdump( "address", address, sizeof( struct HWND )); | |
} | |
else { | |
printf("%-16s: No such window.\n", __FUNCTION__); | |
} | |
} | |
else { | |
printf( "%s\n", response ); | |
} | |
} | |
else { | |
printf("Connection Error %d [%s].\n", status, response ); | |
} | |
return handle; | |
} | |
static inline bool | |
XcbAtomCreateFrom( Window hWnd, xcb_atom_t xcbAtomProp, | |
xcb_atom_t xcbAtomType, | |
uint8_t xcbAtomBitLen, | |
uint32_t xcbAtomLength, | |
void* data ) | |
{ | |
uint32_t status; | |
const char* message; | |
if ((message = XcbQueryServerError( &status ))) { | |
xcb_change_property(connection, | |
XCB_PROP_MODE_REPLACE, | |
hWnd->hWnd, | |
xcbAtomProp, | |
xcbAtomType, | |
xcbAtomBitLen, | |
xcbAtomLength, | |
data); | |
return true; | |
} | |
else { | |
printf("Connection Error %d [%s].\n", status, message ); | |
} | |
return false; | |
} | |
__attribute__((constructor)) | |
void DisplayConnect() { | |
connection = xcb_connect (nullptr, nullptr); | |
xcb_atom_t atom; | |
if ((atom = (xcb_atom_t) QueryAtom( "WM_PROTOCOLS" ))) { | |
WM = atom; | |
if ((atom = (xcb_atom_t) QueryAtom( "WM_DELETE_WINDOW" ))) { | |
display = xcb_setup_roots_iterator (xcb_get_setup (connection)).data; | |
WindowCloseEvent = atom; | |
} | |
else { | |
xcb_disconnect (connection); | |
exit(-1); | |
} | |
} | |
else { | |
xcb_disconnect (connection); | |
exit(-1); | |
} | |
} | |
HWND CreateWindow(LPCTSTR lpClassName, | |
LPCTSTR lpWindowName, | |
DWORD dwStyle, | |
int x, | |
int y, | |
int nWidth, | |
int nHeight, | |
HWND hWndParent, | |
HMENU hMenu, | |
HINSTANCE hInstance, | |
LPVOID lpParam) | |
{ | |
Window window = (Window) calloc(1, sizeof(window_t)); | |
Window parent = (Window) nullptr; | |
if (hWndParent != nullptr) { | |
parent = hWndParent->window; | |
} | |
if (parent != nullptr) { | |
parent->nChildren ++; | |
size_t nWinBytes = sizeof(struct Window); | |
parent->children = reallocarray( parent->children, | |
nWinBytes, | |
parent->nChildren); | |
parent->children[parent->nChildren] = (HWND) window; | |
} | |
window->hWnd = xcb_generate_id (connection); | |
window->context = xcb_generate_id (connection); | |
window->caption = strdup(lpWindowName); | |
void* caption = (void *) window->caption; | |
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES; | |
uint32_t values[2] = {display->white_pixel, 0}; | |
xcb_create_gc (connection, window->context, display->root, mask, values); | |
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; | |
xcb_drawable_t handle = | |
(parent == nullptr || parent == 0? window: parent)->hWnd; | |
xcb_rectangle_t position = { .x = x, .y = y, .height = nHeight, .width = nWidth }; | |
window->rect = position; | |
xcb_create_window( connection, // connection | |
XCB_COPY_FROM_PARENT, // depth | |
handle, // window Id | |
display->root, // parent window | |
x, y, // x, y | |
nWidth, nHeight, // width, height | |
10, // border_width | |
XCB_WINDOW_CLASS_INPUT_OUTPUT, // class | |
display->root_visual, // visual | |
mask, values ); // masks | |
xcb_flush (connection); | |
HWND hWnd = (HWND) | |
calloc(1, sizeof(struct HWND)); | |
hWnd->window = window; | |
if (parent == nullptr || parent == 0) { | |
XcbAtomCreate( window, "WM_PROTOCOLS", XCB_ATOM_ATOM, 32, 1, &WindowCloseEvent ); | |
printf("%-16s: HWND[%" PRIx64 "] is at address %lx\n", __FUNCTION__, (intptr_t) window->hWnd, (intptr_t) hWnd); | |
printf("%-16s: Creating top-level window.\n", __FUNCTION__); | |
FindWindow(window->hWnd); | |
} | |
else { | |
hWnd->window = parent; | |
} | |
XcbAtomCreate( window, hPropName, XCB_ATOM_INTEGER, 32, __POINTER_WIDTH__ / 32, hWnd ); | |
XcbAtomCreate( window, "WM_NAME", XCB_ATOM_STRING, 8, strlen(window->caption), caption ); | |
return hWnd; | |
} | |
bool DestroyWindow(HWND hWnd) { | |
uint32_t i, result; | |
const char* message; | |
Window window = (Window) hWnd, | |
wChild = nullptr; | |
if (window->nChildren > 0) { | |
for ( i = 0 | |
, wChild = ((window_t *) window->children[ i ]) | |
; i < window->nChildren | |
; i++ | |
, wChild = ((window_t *) window->children[ i ])) | |
{ | |
if (DestroyWindow((HWND) wChild) == false) { | |
puts( "Error destroying window." ); | |
exit(1); | |
} | |
} | |
} else { | |
xcb_destroy_window_checked (connection, window->hWnd); | |
xcb_flush (connection); | |
if (( message = XcbQueryServerError( &result ))) { | |
printf("%-8d Server error: %s\n", result, message ); | |
exit(1); | |
} else { | |
printf("Destroying window id %d [%s], Message address is %p, error code is %d.\n", | |
window->hWnd, window->caption, message, result); | |
free(window); | |
return true; | |
} | |
} | |
return false; | |
} | |
bool ShowWindow(HWND hWnd, bool nCmdShow) { | |
xcb_generic_error_t *response = nullptr; | |
Window window = (Window) hWnd->window; | |
xcb_void_cookie_t (*method)(xcb_connection_t *conn, xcb_window_t window) = | |
nCmdShow? xcb_map_window_checked: xcb_unmap_window_checked; | |
const char* stubText = nCmdShow? "xcb_map_window_checked": "xcb_unmap_window_checked"; | |
xcb_void_cookie_t cookie = method(connection, window->hWnd); | |
if ((response = xcb_request_check(connection, cookie))) { | |
printf("%s(%s): Invalid hWnd id=(%d). Err=%04d\n", __FUNCTION__, stubText, window->hWnd, response->error_code); | |
} | |
else { | |
printf("%-16s: Method (%s) dispatched.\n", __FUNCTION__, stubText); | |
return true; | |
} | |
return false; | |
} | |
int main () { | |
/* Open the connection to the X server */ | |
HWND window = CreateWindow( | |
nullptr, | |
"My Window Title", | |
0, | |
0, 0, | |
350, 350, | |
0, | |
0, | |
0, | |
0); | |
HWND control = CreateWindow( | |
nullptr, | |
"Child window", | |
0, | |
0, 0, | |
75, 75, | |
window, | |
0, | |
0, | |
0); | |
/* Map the window on the screen and flush*/ | |
ShowWindow(window, true); | |
/* draw primitives */ | |
int running = 1; | |
xcb_flush (connection); | |
for ( xcb_generic_event_t *event = xcb_wait_for_event (connection) | |
; running && event != nullptr | |
; event = xcb_wait_for_event (connection)) | |
{ | |
switch (event->response_type & ~0x80) { | |
/* | |
* flush the request | |
*/ | |
case XCB_EXPOSE: | |
xcb_flush (connection); | |
break; | |
case XCB_CLIENT_MESSAGE: { | |
xcb_client_message_event_t client = | |
*(xcb_client_message_event_t *) event; | |
if( client.data.data32[0] == WindowCloseEvent) { | |
HWND window = 0; | |
if (( window = FindWindow( client.window ))) { | |
printf("Source is HWND %lx\n", (intptr_t) window); | |
//DestroyWindow(window); | |
} | |
} | |
} break; | |
default: | |
/* Unknown event type, ignore it */ | |
break; | |
} | |
free (event); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment