/* sortix.c * Sortix display(1) support * (c) 2021 Juhani 'nortti' Krekelä * This file is a part of the Links program, released under GPL. */ #include "cfg.h" #ifdef GRDRV_SORTIX #include #include #include #include #include #include "links.h" struct window_data { uint32_t window_id; int modifiers; uint32_t *buffer; }; static int default_window_width = 600; static int default_window_height = 500; static struct display_connection* connection; static struct display_event_handlers event_handlers; struct graphics_driver sortix_driver; static struct graphics_device *current_dev; static void on_disconnect(void *ctx) { if (current_dev->keyboard_handler) current_dev->keyboard_handler(current_dev, KBD_CTRL_C, 0); } static void on_quit(void *ctx, uint32_t window_id) { struct window_data *window_data = current_dev->driver_data; if (window_id != window_data->window_id) return; if (current_dev->keyboard_handler) current_dev->keyboard_handler(current_dev, KBD_CLOSE, 0); } static void on_keyboard(void *ctx, uint32_t window_id, uint32_t codepoint) { (void) ctx; struct window_data *window_data = current_dev->driver_data; if (window_id != window_data->window_id) return; int kbkey = KBKEY_DECODE(codepoint); if (kbkey) { switch (kbkey) { case KBKEY_LALT: window_data->modifiers |= KBD_ALT; break; case -KBKEY_LALT: window_data->modifiers &= ~KBD_ALT; break; case KBKEY_LSHIFT: case KBKEY_RSHIFT: window_data->modifiers |= KBD_SHIFT; break; case -KBKEY_LSHIFT: case -KBKEY_RSHIFT: window_data->modifiers &= ~KBD_SHIFT; break; case KBKEY_LCTRL: case KBKEY_RCTRL: window_data->modifiers |= KBD_CTRL; break; case -KBKEY_LCTRL: case -KBKEY_RCTRL: window_data->modifiers &= ~KBD_CTRL; break; } } if (current_dev->keyboard_handler) { if (kbkey) codepoint = 0; switch (codepoint) { case '\n': codepoint = KBD_ENTER; break; case 127: codepoint = KBD_BS; break; case '\t': codepoint = KBD_TAB; break; } switch (kbkey) { case KBKEY_ESC: codepoint = KBD_ESC; break; case KBKEY_LEFT: codepoint = KBD_LEFT; break; case KBKEY_RIGHT: codepoint = KBD_RIGHT; break; case KBKEY_UP: codepoint = KBD_UP; break; case KBKEY_DOWN: codepoint = KBD_DOWN; break; case KBKEY_INSERT: codepoint = KBD_INS; break; case KBKEY_DELETE: codepoint = KBD_DEL; break; case KBKEY_HOME: codepoint = KBD_HOME; break; case KBKEY_END: codepoint = KBD_END; break; case KBKEY_PGUP: codepoint = KBD_PAGE_UP; break; case KBKEY_PGDOWN: codepoint = KBD_PAGE_DOWN; break; case KBKEY_F1: codepoint = KBD_F1; break; case KBKEY_F2: codepoint = KBD_F2; break; case KBKEY_F3: codepoint = KBD_F3; break; case KBKEY_F4: codepoint = KBD_F4; break; case KBKEY_F5: codepoint = KBD_F5; break; case KBKEY_F6: codepoint = KBD_F6; break; case KBKEY_F7: codepoint = KBD_F7; break; case KBKEY_F8: codepoint = KBD_F8; break; case KBKEY_F9: codepoint = KBD_F9; break; case KBKEY_F10: codepoint = KBD_F10; break; case KBKEY_F11: codepoint = KBD_F11; break; case KBKEY_F12: codepoint = KBD_F12; break; } if (codepoint) current_dev->keyboard_handler(current_dev, codepoint, window_data->modifiers); } } static void on_resize(void *ctx, uint32_t window_id, uint32_t width, uint32_t height) { (void) ctx; struct window_data *window_data = current_dev->driver_data; if (window_id != window_data->window_id) return; if (!width || !height) return; free(window_data->buffer); // TODO: Overflow when multiplying window_data->buffer = mem_alloc(width * height * sizeof(uint32_t)); current_dev->size.x2 = width; current_dev->size.y2 = height; if (current_dev->resize_handler) current_dev->resize_handler(current_dev); } static void sortix_process_events(void *data) { (void) data; if (!current_dev) return; while (display_poll_event(connection, &event_handlers) == 0); } static unsigned char *sortix_init_driver(unsigned char *param, unsigned char *display) { if (param) { if (sscanf(param, "%ix%i", &default_window_width, &default_window_height) != 2) return stracpy("-mode syntax is WIDTHxHEIGHT\n"); } (void) display; // Only used by the X11 driver connection = display_connect_default(); if (!connection && errno == ECONNREFUSED) display_spawn(g_argc, g_argv); if (!connection) { char err[256]; snprintf(err, sizeof(err), "Error connecting to display: %s\n", strerror(errno)); return stracpy(err); } sortix_driver.get_color = get_color_fn(sortix_driver.depth); event_handlers.disconnect_handler = on_disconnect; event_handlers.keyboard_handler = on_keyboard; event_handlers.quit_handler = on_quit; event_handlers.resize_handler = on_resize; set_handlers(display_connection_fd(connection), sortix_process_events, NULL, NULL); return NULL; } static struct graphics_device *sortix_init_device(void) { // TODO: Multi-window support struct graphics_device *dev = mem_calloc(sizeof(struct graphics_device)); dev->size.x1 = 0; dev->size.y1 = 0; dev->size.x2 = default_window_width; dev->size.y2 = default_window_height; dev->clip = dev->size; struct window_data *window_data = mem_calloc(sizeof(struct window_data)); dev->driver_data = window_data; window_data->window_id = 0; // TODO: Overflow when multiplying window_data->buffer = mem_alloc(default_window_width * default_window_height * sizeof(uint32_t)); display_create_window(connection, window_data->window_id); display_resize_window(connection, window_data->window_id, dev->size.x2, dev->size.y2); display_show_window(connection, window_data->window_id); current_dev = dev; return dev; } static void sortix_shutdown_device(struct graphics_device *dev) { struct window_data *window_data = dev->driver_data; display_destroy_window(connection, window_data->window_id); free(window_data->buffer); free(window_data); } static void sortix_shutdown_driver(void) { display_disconnect(connection); } static void sortix_after_fork(void) { if (connection) display_disconnect(connection); } static unsigned char *sortix_get_driver_param(void) { return NULL; //TODO: driver parameters? } static int sortix_get_empty_bitmap(struct bitmap *dest) { // TODO: Overflow when multiplying dest->data = mem_alloc_mayfail((size_t)dest->x * (size_t)dest->y * sizeof(uint32_t)); if (!dest->data) return -1; // TODO: Overflow when multiplying dest->skip = (ssize_t)dest->x * sizeof(uint32_t); dest->flags = 0; return 0; } static void sortix_register_bitmap(struct bitmap *bmp) { } static void *sortix_prepare_strip(struct bitmap *bmp, int top, int lines) { if (!bmp->data) return NULL; // TODO: Overflow when multiplying return ((unsigned char *)bmp->data) + bmp->skip * top; } static void sortix_commit_strip(struct bitmap *bmp, int top, int lines) { } static void sortix_unregister_bitmap(struct bitmap *bmp) { if (bmp->data) mem_free(bmp->data); } static void sortix_draw_bitmap(struct graphics_device *dev, struct bitmap *bmp, int x, int y) { CLIP_DRAW_BITMAP struct window_data *window_data = dev->driver_data; int pitch = dev->size.x2; int width = bmp->x; int height = bmp->y; if (!width || !height) return; for (int bitmap_y = 0; bitmap_y < height; bitmap_y++) { for (int bitmap_x = 0; bitmap_x < width; bitmap_x++) { void *pixel_data = ((unsigned char*)bmp->data) + bitmap_y * bmp->skip + bitmap_x * sizeof(uint32_t); uint32_t pixel; memcpy(&pixel, pixel_data, sizeof(uint32_t)); // Set alpha (see sortix_fill_area() for more details) pixel |= 0xff000000; window_data->buffer[(y + bitmap_y) * pitch + x + bitmap_x] = pixel; } } display_render_window(connection, window_data->window_id, 0, 0, dev->size.x2, dev->size.y2, window_data->buffer); } static void sortix_fill_area(struct graphics_device *dev, int x1, int y1, int x2, int y2, long color) { CLIP_FILL_AREA struct window_data *window_data = dev->driver_data; int pitch = dev->size.x2; // Links uses a pixel format where the top byte is clear // Sortix stores alpha there, so set it to 255 uint32_t pixel = color | 0xff000000; for (int y = y1; y < y2; y++) { for (int x = x1; x < x2; x++) { window_data->buffer[y * pitch + x] = pixel; } } display_render_window(connection, window_data->window_id, 0, 0, dev->size.x2, dev->size.y2, window_data->buffer); } static void sortix_draw_hline(struct graphics_device *dev, int x1, int y, int x2, long color) { CLIP_DRAW_HLINE sortix_fill_area(dev, x1, y, x2, y + 1, color); } static void sortix_draw_vline(struct graphics_device *dev, int x, int y1, int y2, long color) { CLIP_DRAW_VLINE sortix_fill_area(dev, x, y1, x + 1, y2, color); } static void sortix_set_title(struct graphics_device *dev, unsigned char *title) { struct window_data *window_data = dev->driver_data; display_title_window(connection, window_data->window_id, title); } struct graphics_driver sortix_driver = { (unsigned char *)"sortix", sortix_init_driver, sortix_init_device, sortix_shutdown_device, sortix_shutdown_driver, NULL, // emergency_shutdown sortix_after_fork, sortix_get_driver_param, NULL, // get_af_unix_name NULL, // get_margin NULL, // set_margin sortix_get_empty_bitmap, sortix_register_bitmap, sortix_prepare_strip, sortix_commit_strip, sortix_unregister_bitmap, sortix_draw_bitmap, NULL, // get_color, set in sortix_init_driver() sortix_fill_area, sortix_draw_hline, sortix_draw_vline, NULL, // scroll NULL, // set_clip_area NULL, // flush NULL, // block NULL, // unblock NULL, // set_palette NULL, // get_real_colors sortix_set_title, NULL, // exec NULL, // set_clipboard_text NULL, // get_clipboard_text (24 << 3) | 4, // depth: 24bpp, 4 bytes per pixel 0, 0, // size (x, y), unused GD_DONT_USE_SCROLL | GD_UNICODE_KEYS, //flags NULL, // param }; #endif /* GRDRV_SORTIX */