Files
BBeOS/ui/compositor/q20-compositor.c
Eliott 7b53cde2ae
Some checks failed
CI / markdown-lint (push) Failing after 14s
Complete BBeOS project implementation with BlackBerry-inspired website
- Updated .gitignore with comprehensive exclusions for build artifacts, IDE files, and OS-specific files
- Created BlackBerry-inspired website with Heroicons and Gitea integration
- Added complete project structure with all 7 phases implemented
- Included kernel drivers, UI components, telephony stack, and packaging tools
- Added emulation scripts for testing and development
- Comprehensive documentation for all development phases
- Security analysis and hardware testing guides
- SDK and application framework for third-party development
2025-08-01 10:20:28 +02:00

361 lines
12 KiB
C

/*
* Q20 Wayland Compositor
* BlackBerry Classic Q20 Display Server
*
* A lightweight Wayland compositor optimized for the Q20's
* 720x720 square display and keyboard navigation.
*/
#include <wayland-server.h>
#include <wlr/backend.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
struct q20_server {
struct wl_display *wl_display;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_compositor *compositor;
struct wlr_seat *seat;
struct wlr_output *output;
struct wlr_xdg_shell *xdg_shell;
struct wl_listener new_output;
struct wl_listener new_input;
struct wl_listener new_xdg_surface;
struct wl_list views;
struct wl_list keyboards;
struct wlr_view *focused_view;
struct wlr_keyboard *focused_keyboard;
};
struct q20_view {
struct wl_list link;
struct q20_server *server;
struct wlr_xdg_surface *xdg_surface;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener destroy;
struct wl_listener request_move;
struct wl_listener request_resize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen;
int x, y;
bool maximized;
bool fullscreen;
};
struct q20_keyboard {
struct wl_list link;
struct q20_server *server;
struct wlr_input_device *device;
struct wl_listener key;
struct wl_listener modifiers;
struct wl_listener destroy;
struct xkb_state *xkb_state;
xkb_keycode_t repeat_keycode;
uint32_t repeat_time;
struct wl_event_source *repeat_source;
};
static void focus_view(struct q20_server *server, struct q20_view *view) {
if (server->focused_view == view) {
return;
}
if (server->focused_view) {
struct wlr_xdg_surface *previous_surface = server->focused_view->xdg_surface;
wlr_xdg_toplevel_set_activated(previous_surface, false);
}
server->focused_view = view;
if (view) {
wlr_xdg_toplevel_set_activated(view->xdg_surface, true);
wlr_seat_keyboard_notify_enter(server->seat, view->xdg_surface->surface,
NULL, 0, NULL);
}
}
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct q20_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct q20_server *server = keyboard->server;
struct wlr_event_keyboard_key *event = data;
struct wlr_seat *seat = server->seat;
// Get the keycode and translate it to a keysym
xkb_keycode_t keycode = event->keycode + 8;
xkb_keysym_t sym = xkb_state_key_get_one_sym(keyboard->xkb_state, keycode);
// Handle Q20-specific key combinations
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
switch (sym) {
case XKB_KEY_Escape:
// Exit compositor
wl_display_terminate(server->wl_display);
break;
case XKB_KEY_F1:
// Switch to next view
// TODO: Implement view switching
break;
case XKB_KEY_F2:
// Toggle maximize
if (server->focused_view) {
server->focused_view->maximized = !server->focused_view->maximized;
if (server->focused_view->maximized) {
wlr_xdg_toplevel_set_maximized(server->focused_view->xdg_surface, true);
} else {
wlr_xdg_toplevel_set_maximized(server->focused_view->xdg_surface, false);
}
}
break;
case XKB_KEY_F3:
// Toggle fullscreen
if (server->focused_view) {
server->focused_view->fullscreen = !server->focused_view->fullscreen;
if (server->focused_view->fullscreen) {
wlr_xdg_toplevel_set_fullscreen(server->focused_view->xdg_surface, true);
} else {
wlr_xdg_toplevel_set_fullscreen(server->focused_view->xdg_surface, false);
}
}
break;
}
}
wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
}
static void keyboard_modifiers_notify(struct wl_listener *listener, void *data) {
struct q20_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
struct q20_server *server = keyboard->server;
wlr_seat_keyboard_notify_modifiers(server->seat, &keyboard->device->keyboard->modifiers);
}
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct q20_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->link);
free(keyboard);
}
static void server_new_keyboard(struct q20_server *server, struct wlr_input_device *device) {
struct q20_keyboard *keyboard = calloc(1, sizeof(struct q20_keyboard));
keyboard->server = server;
keyboard->device = device;
// Get the keymap
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
keyboard->xkb_state = xkb_state_new(keymap);
xkb_keymap_unref(keymap);
xkb_context_unref(context);
wlr_keyboard_set_keymap(device->keyboard, keymap);
wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
keyboard->key.notify = keyboard_key_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->modifiers.notify = keyboard_modifiers_notify;
wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers);
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->events.destroy, &keyboard->destroy);
wl_list_insert(&server->keyboards, &keyboard->link);
// Set this keyboard as the current one
wlr_seat_set_keyboard(server->seat, device);
}
static void server_new_input_notify(struct wl_listener *listener, void *data) {
struct q20_server *server = wl_container_of(listener, server, new_input);
struct wlr_input_device *device = data;
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
server_new_keyboard(server, device);
break;
default:
break;
}
}
static void view_map_notify(struct wl_listener *listener, void *data) {
struct q20_view *view = wl_container_of(listener, view, map);
struct q20_server *server = view->server;
// Center the view on the 720x720 display
view->x = (720 - view->xdg_surface->surface->current.width) / 2;
view->y = (720 - view->xdg_surface->surface->current.height) / 2;
focus_view(server, view);
}
static void view_unmap_notify(struct wl_listener *listener, void *data) {
struct q20_view *view = wl_container_of(listener, view, unmap);
struct q20_server *server = view->server;
if (server->focused_view == view) {
focus_view(server, NULL);
}
}
static void view_destroy_notify(struct wl_listener *listener, void *data) {
struct q20_view *view = wl_container_of(listener, view, destroy);
wl_list_remove(&view->link);
free(view);
}
static void server_new_xdg_surface_notify(struct wl_listener *listener, void *data) {
struct q20_server *server = wl_container_of(listener, server, new_xdg_surface);
struct wlr_xdg_surface *xdg_surface = data;
if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
return;
}
struct q20_view *view = calloc(1, sizeof(struct q20_view));
view->server = server;
view->xdg_surface = xdg_surface;
view->map.notify = view_map_notify;
wl_signal_add(&xdg_surface->events.map, &view->map);
view->unmap.notify = view_unmap_notify;
wl_signal_add(&xdg_surface->events.unmap, &view->unmap);
view->destroy.notify = view_destroy_notify;
wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
wl_list_insert(&server->views, &view->link);
}
static void output_frame_notify(struct wl_listener *listener, void *data) {
struct q20_server *server = wl_container_of(listener, server, output_frame);
struct wlr_output *output = data;
struct wlr_renderer *renderer = server->renderer;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_output_make_current(output, NULL);
wlr_renderer_begin(renderer, output->width, output->height);
float color[4] = {0.1f, 0.1f, 0.1f, 1.0f};
wlr_renderer_clear(renderer, color);
// Render all views
struct q20_view *view;
wl_list_for_each_reverse(view, &server->views, link) {
if (!view->xdg_surface->mapped) {
continue;
}
struct wlr_box box = {
.x = view->x,
.y = view->y,
.width = view->xdg_surface->surface->current.width,
.height = view->xdg_surface->surface->current.height,
};
wlr_renderer_scissor(renderer, &box);
wlr_render_texture(renderer, view->xdg_surface->surface->texture, output->transform_matrix, box.x, box.y, 1.0f);
}
wlr_renderer_scissor(renderer, NULL);
wlr_renderer_end(renderer);
wlr_output_swap_buffers(output, NULL, NULL);
}
static void server_new_output_notify(struct wl_listener *listener, void *data) {
struct q20_server *server = wl_container_of(listener, server, new_output);
struct wlr_output *output = data;
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
// Configure for Q20's 720x720 display
wlr_output_set_custom_mode(output, 720, 720, 60000);
wlr_output_create_global(output);
server->output = output;
output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.frame, &output->frame);
}
int main(int argc, char *argv[]) {
wlr_log_init(WLR_DEBUG, NULL);
struct q20_server server = {0};
server.wl_display = wl_display_create();
server.backend = wlr_backend_autocreate(server.wl_display);
server.renderer = wlr_renderer_autocreate(server.backend);
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
server.compositor = wlr_compositor_create(server.wl_display, server.renderer);
wlr_export_dmabuf_manager_v1_create(server.wl_display);
wlr_screencopy_manager_v1_create(server.wl_display);
wlr_data_device_manager_create(server.wl_display);
server.seat = wlr_seat_create(server.wl_display, "seat0");
server.xdg_shell = wlr_xdg_shell_create(server.wl_display);
server.new_xdg_surface.notify = server_new_xdg_surface_notify;
wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface);
wl_list_init(&server.views);
wl_list_init(&server.keyboards);
server.new_output.notify = server_new_output_notify;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
server.new_input.notify = server_new_input_notify;
wl_signal_add(&server.backend->events.new_input, &server.new_input);
const char *socket = wl_display_add_socket_auto(server.wl_display);
if (!socket) {
wlr_backend_destroy(server.backend);
return 1;
}
if (!wlr_backend_start(server.backend)) {
wlr_backend_destroy(server.backend);
wl_display_destroy(server.wl_display);
return 1;
}
setenv("WAYLAND_DISPLAY", socket, true);
wlr_log(WLR_INFO, "Running Q20 compositor on WAYLAND_DISPLAY=%s", socket);
wl_display_run(server.wl_display);
wl_display_destroy(server.wl_display);
return 0;
}