#include #include #include #include #include #include #include #include #include "../../Common/bin.h" #include "../../Common/bits.h" #include "../../Common/buf.h" #include "../../Common/cfg.h" #include "../../Common/lzw.h" #include "../../Common/mat.h" #include "../../Common/std.h" #include "../../Common/str.h" #include "../draw.h" #include "display.h" /* https://hereket.com/posts/from-scratch-x11-windowing/ */ /* https://gaultier.github.io/blog/write_a_video_game_from_scratch_like_1987.html */ static struct { uint32_t id; uint32_t colormap; uint32_t white; uint32_t black; uint32_t input_mask; uint16_t width; uint16_t height; uint16_t width_mm; uint16_t height_mm; uint16_t maps_min; uint16_t maps_max; uint32_t root_visual_id; uint8_t backing_store; uint8_t save_unders; uint8_t root_depth; uint8_t depths_count; } plr_x11_root_screen; static struct { uint32_t resource_id_base; uint32_t resource_id_mask; } plr_x11_connection; bool quited = false; bool fileread(void *ctx, char *into, uint32_t bytes) { return com_std_read(ctx, into, bytes) == bytes; } bool fileskip(void *ctx, uint32_t bytes) { return com_std_sfwd(ctx, bytes); } #define AUTH_ENTRY_MAGIC_COOKIE "MIT-MAGIC-COOKIE-1" static struct plr_x11_auth_entry { uint16_t family; struct com_buf *auth_name; struct com_buf *auth_data; bool success; } plr_x11_read_auth_entry(struct com_bin *b, struct com_buf *buf) { struct plr_x11_auth_entry result = {0}; if (!com_bin_read_u16(b, &result.family)) goto BREAK; uint16_t addres_len; if (!com_bin_read_u16(b, &addres_len)) goto BREAK; if (!com_bin_skip(b, addres_len)) goto BREAK; uint16_t display_len; if (!com_bin_read_u16(b, &display_len)) goto BREAK; if (!com_bin_skip(b, display_len)) goto BREAK; uint16_t name_len; if (!com_bin_read_u16(b, &name_len)) goto BREAK; uint32_t name_idx; if (!com_bin_read_bufbi(b, buf, &name_idx, name_len)) goto BREAK; uint16_t data_len; if (!com_bin_read_u16(b, &data_len)) goto BREAK; uint32_t data_idx; if (!com_bin_read_bufbi(b, buf, &data_idx, data_len)) goto BREAK; result.auth_name = com_buf_index_as(struct com_buf, buf, name_idx); result.auth_data = com_buf_index_as(struct com_buf, buf, data_idx); result.success = true; return result; BREAK: com_std_printf("Failure to initialize X11\n"); return result; } static bool plr_x11_handshake(com_std_fd xfd, struct plr_x11_auth_entry auth_entry) { struct { uint8_t endianness; uint8_t pad0; uint16_t major_version; uint16_t minor_version; uint16_t auth_len; uint16_t auth_data_len; uint16_t pad1; } request = { .endianness = 'l', .major_version = 11, .auth_len = sizeof(AUTH_ENTRY_MAGIC_COOKIE) - 1, // IS -1 NEEDED? .auth_data_len = auth_entry.auth_data->len, }; if (!com_std_write(xfd, &request, sizeof request)) return false; if (!com_std_write(xfd, AUTH_ENTRY_MAGIC_COOKIE, sizeof AUTH_ENTRY_MAGIC_COOKIE - 1)) return false; /* Use 2 bytes back for padding. */ if (!com_std_write(xfd, &auth_entry.auth_data->data[-2], auth_entry.auth_data->len + 2)) return false; struct { uint8_t success; uint8_t pad1; uint16_t major_version; uint16_t minor_version; uint16_t length; } response; if (!com_std_read(xfd, &response, sizeof response)) return false; if (response.success != 1) return false; struct { uint32_t release_number; uint32_t resource_id_base; uint32_t resource_id_mask; uint32_t motion_buffer_size; uint16_t vendor_length; uint16_t maximum_request_length; uint8_t screens_in_root_count; uint8_t formats_count; uint8_t image_byte_order; uint8_t bitmap_format_bit_order; uint8_t bitmap_format_scanline_unit; uint8_t bitmap_format_scanline_pad; uint8_t min_keycode; uint8_t max_keycode; uint32_t pad2; } dyn_response; if (!com_std_read(xfd, &dyn_response, sizeof dyn_response)) return false; /* Skip vendor and format info. */ char scratch[1024]; uint32_t skip = com_bits_nalign(dyn_response.vendor_length, 4); com_std_assert(skip <= sizeof scratch); com_std_assert(8 * dyn_response.formats_count <= sizeof scratch); if (!com_std_read(xfd, scratch, skip)) return false; if (!com_std_read(xfd, scratch, 8 * dyn_response.formats_count)) return false; if (!com_std_read(xfd, &plr_x11_root_screen, sizeof plr_x11_root_screen)) return false; plr_x11_connection.resource_id_base = dyn_response.resource_id_base; plr_x11_connection.resource_id_mask = dyn_response.resource_id_mask; return true; } static inline uint32_t plr_x11_next_id(void) { static uint32_t cur_id = 0; cur_id = 1 + ((plr_x11_connection.resource_id_mask & (cur_id)) | plr_x11_connection.resource_id_base); return cur_id; } static uint32_t plr_x11_create_graphical_context(com_std_fd xfd) { uint32_t const gc_id = plr_x11_next_id(); struct { uint8_t opcode; uint8_t pad1; uint16_t length; uint32_t id; uint32_t drawable; uint32_t bitmask; uint32_t value1; } request = { .opcode = 55, .length = 5, .id = gc_id, .drawable = plr_x11_root_screen.id, .bitmask = 8, /* TODO: Figure out. */ .value1 = 0x0000ff00, }; if (com_std_write(xfd, &request, sizeof request)) return gc_id; else return 0; } static uint32_t plr_x11_create_window(com_std_fd xfd) { /* TODO: Use the ones provided in headers ? */ #define FLAG_WIN_BG_PIXEL 2 #define FLAG_WIN_EVENT 0x800 #define EVENT_FLAG_EXPOSURE 0x8000 #define EVENT_FLAG_KEY_PRESS 0x1 #define EVENT_FLAG_KEY_RELEASE 0x2 #define EVENT_FLAG_BUTTON_PRESS 0x4 #define EVENT_FLAG_BUTTON_RELEASE 0x8 uint32_t const window_id = plr_x11_next_id(); struct { uint8_t opcode; uint8_t depth; uint16_t request_length; uint32_t window_id; uint32_t parent_id; uint16_t x; uint16_t y; uint16_t width; uint16_t height; uint16_t border_width; uint16_t class; uint32_t root_visual_id; uint32_t bitmask; uint32_t value1; uint32_t value2; } request = { .opcode = 1, .depth = 32, .request_length = 8 + 2, .window_id = window_id, .parent_id = plr_x11_root_screen.id, .width = COM_CFG_WIDTH, .height = COM_CFG_HEIGHT, .border_width = 0, .class = 1, .root_visual_id = plr_x11_root_screen.root_visual_id, .bitmask = FLAG_WIN_BG_PIXEL | FLAG_WIN_EVENT, .value1 = 0x00ffff00, .value2 = EVENT_FLAG_EXPOSURE | EVENT_FLAG_BUTTON_RELEASE | EVENT_FLAG_BUTTON_PRESS | EVENT_FLAG_KEY_PRESS | EVENT_FLAG_KEY_RELEASE, }; if (com_std_write(xfd, &request, sizeof request)) return window_id; else return 0; } static bool plr_x11_show_window(com_std_fd xfd, uint32_t window_id) { struct { uint8_t opcode; uint8_t pad1; uint16_t request_length; uint32_t window_id; } request = { .opcode = 8, .request_length = 2, .window_id = window_id, }; return com_std_write(xfd, &request, sizeof request); } static uint32_t plr_x11_create_pixmap(com_std_fd xfd, uint32_t window_id) { uint32_t const pixmap_id = plr_x11_next_id(); struct { uint8_t opcode; uint8_t depth; uint16_t length; uint32_t pixmap_id; uint32_t drawable; uint16_t width; uint16_t height; } request = { .opcode = 53, .depth = 32, .length = 4, .pixmap_id = pixmap_id, .drawable = window_id, .width = COM_CFG_WIDTH, .height = COM_CFG_HEIGHT, }; if (com_std_write(xfd, &request, sizeof request)) return pixmap_id; else return 0; } static bool plr_x11_put_framebuffer(com_std_fd xfd, uint32_t gc_id, uint32_t window_id, uint32_t pixmap_id, uint32_t pixels[COM_CFG_WIDTH * COM_CFG_HEIGHT]) { uint32_t const padded = com_bits_nalign(4 * COM_CFG_WIDTH * COM_CFG_HEIGHT, 4); struct { uint8_t opcode; uint8_t format; uint16_t request_length; uint32_t drawable_id; uint32_t gc_id; uint16_t width; uint16_t height; uint16_t dst_x; uint16_t dst_y; uint8_t left_pad; uint8_t depth; uint16_t pad1; } request = { .opcode = 72, .format = 2, // ZPixmap .request_length = 6 + padded / 4, .drawable_id = pixmap_id, .gc_id = gc_id, .width = COM_CFG_WIDTH, .height = COM_CFG_HEIGHT, .dst_x = 0, .dst_y = 0, .depth = 32, }; if (!com_std_write(xfd, &request, sizeof request)) return false; if (!com_std_write(xfd, pixels, 4 * COM_CFG_WIDTH * COM_CFG_HEIGHT)) return false; /* Use request memory for pad, it doesn't matter what we write. */ uint32_t const padfinal = padded - (4 * COM_CFG_WIDTH * COM_CFG_HEIGHT); if (padfinal && !com_std_write(xfd, &request, padfinal)) return false; return true; } static uint32_t pixel_buffer[COM_CFG_WIDTH * COM_CFG_HEIGHT]; // TODO: correct error handling. extern int plr_display_x11_main(int argc, char *argv[]) { char test_string[] = "what is this?"; char *compressed_string = NULL; uint32_t compressed_string_sz = 0; struct com_lzw_table table = com_lzw_infer_table(test_string, sizeof(test_string)); bool test = com_lzw_compress(&table, test_string, sizeof(test_string), &compressed_string, &compressed_string_sz); com_lzw_free_table(&table); com_std_assert(test); com_mat_t m0 = com_mat_identity(); com_mat_t m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; for (int i = 0; i < 16; ++i) m1.a[i] *= COM_FIX_FRACUNIT; com_mat_t t0 = com_mat_mul(m1, m0); com_mat_t t1 = com_mat_mul_reodered(com_mat_reoder(m1), m0); com_fix_print(t0.a[4]); com_fix_print(t1.a[4]); // com_vec_t v0 = com_mat_vec_project(com_mat_reoder(m0), com_vec_identity()); // com_fix_print(v0.a[0]); // com_fix_run_tests(); // com_fix_run_bench(); // com_mat_run_bench(); /* TODO: Move init to separate func. */ /* Open X11 socket for communication. */ com_std_fd const xfd = com_std_socket(false); if (!xfd) { com_std_printf("Failure to initialize X11\n"); return -1; } if (!com_std_connect(xfd, false, "/tmp/.X11-unix/X0")) { com_std_printf("Failure to initialize X11\n"); return -1; } /* First we need to authenticate X11 connection, which requires some funk. */ /* We assume that $XAUTHORITY is set and I hope it is true. */ char const *xauthpath = NULL; for (char **envp = &argv[argc + 1]; *envp != NULL; ++envp) { static const struct com_str xauthlit = com_str_lit("XAUTHORITY="); if (com_str_ext_prefixed(*envp, xauthlit)) { xauthpath = &((*envp)[xauthlit.len]); break; } } /* Open it and parse. */ com_std_fd const xauthfd = com_std_open(xauthpath, COM_STD_OPEN_READ); if (xauthfd < 0) { com_std_printf("Failure to initialize X11\n"); return -1; } com_buf_make_on_stack(char, xauthbuf, 1024, 0); struct com_bin xauthbin = { .ctx = (void *)(int64_t)xauthfd, .flags = COM_BIN_BIG_ENDIAN, .interface = {.readn = fileread, .skipn = fileskip}}; /* Loop over auth entries until we find magic cookie one. */ struct plr_x11_auth_entry auth_entry; do { com_buf_clear(xauthbuf); auth_entry = plr_x11_read_auth_entry(&xauthbin, xauthbuf); } while (auth_entry.success && auth_entry.family != 256); if (!auth_entry.success) { com_std_printf("Failure to initialize X11\n"); return -1; } com_std_close(xauthfd); /* TODO: Check for auth_entry.name == MAGIC ? */ if (!plr_x11_handshake(xfd, auth_entry)) { com_std_printf("Failure to initialize X11\n"); return -1; } com_buf_free(xauthbuf); uint32_t const gc = plr_x11_create_graphical_context(xfd); if (!gc) { com_std_printf("Failure to initialize X11\n"); return -1; } uint32_t const win = plr_x11_create_window(xfd); if (!win) { com_std_printf("Failure to initialize X11\n"); return -1; } uint32_t const fb = plr_x11_create_pixmap(xfd, win); if (!fb) { com_std_printf("Failure to initialize X11\n"); return -1; } // Display *display = XOpenDisplay(NULL); // if (NULL == display) { // com_std_printf("Failed to initialize display"); // return -1; // } // Window root = DefaultRootWindow(display); // if (None == root) { // com_std_printf("No root window found"); // XCloseDisplay(display); // return -1; // } // int screen = DefaultScreen(display); // Visual *visual = DefaultVisual(display, screen); // int depth = DefaultDepth(display, screen); // Window window = // XCreateSimpleWindow(display, root, 0, 0, WIDTH, HEIGHT, 0, 0, // 0xffffffff); // if (None == window) { // com_std_printf("Failed to create window"); // XCloseDisplay(display); // return -1; // } // XSizeHints *hints = XAllocSizeHints(); // if (hints == NULL) // return -1; // // Pinning min and max to the same values disables resizing // hints->flags = PMinSize | PMaxSize; // hints->min_width = hints->max_width = WIDTH; // hints->min_height = hints->max_height = HEIGHT; // XSetWMNormalHints(display, window, hints); // XFree(hints); // XSelectInput(display, window, ExposureMask | KeyPressMask); // XMapWindow(display, window); // GC gc = XCreateGC(display, window, 0, NULL); // Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", // False); XSetWMProtocols(display, window, &wm_delete_window, 1); // // TODO: can't be not true // int bytes_per_pixel = 4; // // char *pixel_buffer = (char *)malloc(WIDTH * HEIGHT * // bytes_per_pixel); char pixel_buffer[WIDTH * HEIGHT * bytes_per_pixel]; // XImage *ximage = XCreateImage(display, visual, depth, ZPixmap, 0, // pixel_buffer, WIDTH, HEIGHT, 32, 0); // if (!ximage) { // com_std_printf("Failed to create XImage\n"); // // com_std_alloc(pixel_buffer, 0); // XCloseDisplay(display); // return -1; // } // int frame = 0; // XEvent event; // while (!quited) { // XNextEvent(display, &event); // switch (event.type) { // case ClientMessage: // if (event.xclient.data.l[0] == wm_delete_window) { // XDestroyWindow(display, window); // quited = true; // } // break; // case Expose: // frame += 2; // plr_draw_screen((uint8_t *)pixel_buffer); // // Draw the complete image onto the window when exposed // XPutImage(display, // window, // Target drawable // gc, // Graphics Context // ximage, // Source XImage // 0, 0, // Source coordinates (x, y) // 0, 0, // Destination coordinates (x, y) // WIDTH, HEIGHT // Dimensions to copy // ); // break; // } // } // XCloseDisplay(display); while (1) { plr_draw_screen((uint8_t *)pixel_buffer); if (!plr_x11_put_framebuffer(xfd, gc, win, fb, pixel_buffer)) { com_std_printf("Failure to drive X11\n"); return -1; } if (!plr_x11_show_window(xfd, win)) { com_std_printf("Failure to drive X11\n"); return -1; } } return 0; }