Skip to content

Commit 1ba83eb

Browse files
authored
Update main.c
1 parent fbcf414 commit 1ba83eb

1 file changed

Lines changed: 352 additions & 0 deletions

File tree

source-code/main.c

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,353 @@
1+
/*
2+
* GameFrame: A minimalist Wayland compositor inspired by Cage,
3+
* optimized for older GPUs. It uses wlroots and focuses on
4+
* basic functionality without relying on modern GPU features
5+
* like advanced shaders or high-performance rendering.
6+
*
7+
* This compositor creates a single fullscreen output and runs
8+
* a specified command (e.g., a terminal or game) inside it.
9+
* It supports basic input and output handling suitable for
10+
* legacy hardware.
11+
*
12+
* Build with: pkg-config --cflags --libs wlroots wayland-server xkbcommon libdrm
13+
* gcc -o gameframe gameframe.c `pkg-config --cflags --libs wlroots wayland-server xkbcommon libdrm`
14+
*
15+
* Run with: ./gameframe /path/to/your/app
16+
*/
117

18+
#include <assert.h>
19+
#include <signal.h>
20+
#include <stdbool.h>
21+
#include <stdlib.h>
22+
#include <string.h>
23+
#include <sys/wait.h>
24+
#include <unistd.h>
25+
#include <wayland-server-core.h>
26+
#include <wlr/backend.h>
27+
#include <wlr/render/allocator.h>
28+
#include <wlr/render/wlr_renderer.h>
29+
#include <wlr/types/wlr_compositor.h>
30+
#include <wlr/types/wlr_data_device.h>
31+
#include <wlr/types/wlr_input_device.h>
32+
#include <wlr/types/wlr_keyboard.h>
33+
#include <wlr/types/wlr_output.h>
34+
#include <wlr/types/wlr_output_layout.h>
35+
#include <wlr/types/wlr_pointer.h>
36+
#include <wlr/types/wlr_scene.h>
37+
#include <wlr/types/wlr_seat.h>
38+
#include <wlr/types/wlr_subcompositor.h>
39+
#include <wlr/types/wlr_xdg_shell.h>
40+
#include <wlr/util/log.h>
41+
42+
struct gameframe_server {
43+
struct wl_display *display;
44+
struct wlr_backend *backend;
45+
struct wlr_renderer *renderer;
46+
struct wlr_allocator *allocator;
47+
struct wlr_scene *scene;
48+
struct wlr_scene_output *scene_output;
49+
50+
struct wlr_xdg_shell *xdg_shell;
51+
struct wlr_compositor *compositor;
52+
struct wlr_subcompositor *subcompositor;
53+
54+
struct wlr_output_layout *output_layout;
55+
struct wlr_seat *seat;
56+
57+
struct wl_listener new_output;
58+
struct wl_listener new_xdg_surface;
59+
struct wl_listener new_input;
60+
struct wl_listener request_set_cursor;
61+
struct wl_listener request_set_selection;
62+
63+
pid_t child_pid;
64+
};
65+
66+
struct gameframe_output {
67+
struct wlr_output *wlr_output;
68+
struct gameframe_server *server;
69+
struct wl_listener frame;
70+
struct wl_listener destroy;
71+
};
72+
73+
struct gameframe_view {
74+
struct wlr_xdg_toplevel *xdg_toplevel;
75+
struct wlr_scene_tree *scene_tree;
76+
struct gameframe_server *server;
77+
struct wl_listener map;
78+
struct wl_listener unmap;
79+
struct wl_listener destroy;
80+
struct wl_listener request_move;
81+
struct wl_listener request_resize;
82+
struct wl_listener request_maximize;
83+
struct wl_listener request_fullscreen;
84+
};
85+
86+
static void output_frame(struct wl_listener *listener, void *data) {
87+
struct gameframe_output *output = wl_container_of(listener, output, frame);
88+
struct wlr_scene *scene = output->server->scene;
89+
struct wlr_scene_output *scene_output = wlr_scene_get_scene_output(scene, output->wlr_output);
90+
91+
wlr_scene_output_commit(scene_output, NULL);
92+
93+
struct timespec now;
94+
clock_gettime(CLOCK_MONOTONIC, &now);
95+
wlr_scene_output_send_frame_done(scene_output, &now);
96+
}
97+
98+
static void output_destroy(struct wl_listener *listener, void *data) {
99+
struct gameframe_output *output = wl_container_of(listener, output, destroy);
100+
wl_list_remove(&output->frame.link);
101+
wl_list_remove(&output->destroy.link);
102+
free(output);
103+
}
104+
105+
static void server_new_output(struct wl_listener *listener, void *data) {
106+
struct gameframe_server *server = wl_container_of(listener, server, new_output);
107+
struct wlr_output *wlr_output = data;
108+
109+
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
110+
111+
struct wlr_output_state state;
112+
wlr_output_state_init(&state);
113+
wlr_output_state_set_enabled(&state, true);
114+
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
115+
if (mode != NULL) {
116+
wlr_output_state_set_mode(&state, mode);
117+
}
118+
wlr_output_commit_state(wlr_output, &state);
119+
wlr_output_state_finish(&state);
120+
121+
struct gameframe_output *output = calloc(1, sizeof(*output));
122+
output->wlr_output = wlr_output;
123+
output->server = server;
124+
output->frame.notify = output_frame;
125+
wl_signal_add(&wlr_output->events.frame, &output->frame);
126+
output->destroy.notify = output_destroy;
127+
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
128+
129+
wlr_output_layout_add_auto(server->output_layout, wlr_output);
130+
131+
server->scene_output = wlr_scene_attach_output(server->scene, wlr_output);
132+
}
133+
134+
static void xdg_toplevel_map(struct wl_listener *listener, void *data) {
135+
struct gameframe_view *view = wl_container_of(listener, view, map);
136+
wlr_scene_tree_set_position(view->scene_tree, 0, 0);
137+
wlr_xdg_toplevel_set_size(view->xdg_toplevel, 0, 0); // Fullscreen implicitly
138+
wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, true);
139+
}
140+
141+
static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) {
142+
struct gameframe_view *view = wl_container_of(listener, view, unmap);
143+
// No-op for now
144+
}
145+
146+
static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {
147+
struct gameframe_view *view = wl_container_of(listener, view, destroy);
148+
wl_list_remove(&view->map.link);
149+
wl_list_remove(&view->unmap.link);
150+
wl_list_remove(&view->destroy.link);
151+
wl_list_remove(&view->request_move.link);
152+
wl_list_remove(&view->request_resize.link);
153+
wl_list_remove(&view->request_maximize.link);
154+
wl_list_remove(&view->request_fullscreen.link);
155+
free(view);
156+
}
157+
158+
static void xdg_toplevel_request_move(struct wl_listener *listener, void *data) {
159+
// No moving in fullscreen
160+
}
161+
162+
static void xdg_toplevel_request_resize(struct wl_listener *listener, void *data) {
163+
// No resizing in fullscreen
164+
}
165+
166+
static void xdg_toplevel_request_maximize(struct wl_listener *listener, void *data) {
167+
// Already maximized/fullscreen
168+
}
169+
170+
static void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data) {
171+
struct gameframe_view *view = wl_container_of(listener, view, request_fullscreen);
172+
struct wlr_xdg_toplevel_set_fullscreen_event *event = data;
173+
wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, event->fullscreen);
174+
}
175+
176+
static void server_new_xdg_surface(struct wl_listener *listener, void *data) {
177+
struct gameframe_server *server = wl_container_of(listener, server, new_xdg_surface);
178+
struct wlr_xdg_surface *xdg_surface = data;
179+
180+
if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
181+
return;
182+
}
183+
184+
struct gameframe_view *view = calloc(1, sizeof(*view));
185+
view->server = server;
186+
view->xdg_toplevel = xdg_surface->toplevel;
187+
view->scene_tree = wlr_scene_xdg_surface_create(&server->scene->tree, xdg_surface);
188+
189+
view->map.notify = xdg_toplevel_map;
190+
wl_signal_add(&xdg_surface->events.map, &view->map);
191+
view->unmap.notify = xdg_toplevel_unmap;
192+
wl_signal_add(&xdg_surface->events.unmap, &view->unmap);
193+
view->destroy.notify = xdg_toplevel_destroy;
194+
wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
195+
196+
view->request_move.notify = xdg_toplevel_request_move;
197+
wl_signal_add(&view->xdg_toplevel->events.request_move, &view->request_move);
198+
view->request_resize.notify = xdg_toplevel_request_resize;
199+
wl_signal_add(&view->xdg_toplevel->events.request_resize, &view->request_resize);
200+
view->request_maximize.notify = xdg_toplevel_request_maximize;
201+
wl_signal_add(&view->xdg_toplevel->events.request_maximize, &view->request_maximize);
202+
view->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
203+
wl_signal_add(&view->xdg_toplevel->events.request_fullscreen, &view->request_fullscreen);
204+
}
205+
206+
static void process_keyboard(struct gameframe_server *server, struct wlr_keyboard *keyboard) {
207+
wlr_seat_set_keyboard(server->seat, keyboard);
208+
wlr_keyboard_set_repeat_info(keyboard, 25, 600);
209+
}
210+
211+
static void server_new_input(struct wl_listener *listener, void *data) {
212+
struct gameframe_server *server = wl_container_of(listener, server, new_input);
213+
struct wlr_input_device *device = data;
214+
215+
switch (device->type) {
216+
case WLR_INPUT_DEVICE_KEYBOARD: {
217+
struct wlr_keyboard *kb = wlr_keyboard_from_input_device(device);
218+
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
219+
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
220+
wlr_keyboard_set_keymap(kb, keymap);
221+
xkb_keymap_unref(keymap);
222+
xkb_context_unref(context);
223+
process_keyboard(server, kb);
224+
break;
225+
}
226+
case WLR_INPUT_DEVICE_POINTER:
227+
wlr_seat_set_capabilities(server->seat, WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD);
228+
break;
229+
default:
230+
break;
231+
}
232+
233+
uint32_t caps = WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD;
234+
wlr_seat_set_capabilities(server->seat, caps);
235+
}
236+
237+
static void request_set_cursor(struct wl_listener *listener, void *data) {
238+
struct gameframe_server *server = wl_container_of(listener, server, request_set_cursor);
239+
struct wlr_seat_pointer_request_set_cursor_event *event = data;
240+
struct wlr_seat_client *client = event->seat_client;
241+
242+
if (client == NULL) {
243+
return;
244+
}
245+
246+
wlr_seat_pointer_surface_set_cursor(server->seat, client, event->surface, event->hotspot_x, event->hotspot_y);
247+
}
248+
249+
static void request_set_selection(struct wl_listener *listener, void *data) {
250+
struct gameframe_server *server = wl_container_of(listener, server, request_set_selection);
251+
struct wlr_seat_request_set_selection_event *event = data;
252+
wlr_seat_set_selection(server->seat, event->source, event->serial);
253+
}
254+
255+
int main(int argc, char *argv[]) {
256+
wlr_log_init(WLR_DEBUG, NULL);
257+
258+
if (argc < 2) {
259+
fprintf(stderr, "Usage: %s <command>\n", argv[0]);
260+
return 1;
261+
}
262+
263+
struct gameframe_server server = {0};
264+
server.display = wl_display_create();
265+
if (server.display == NULL) {
266+
wlr_log(WLR_ERROR, "Cannot create wayland display");
267+
return 1;
268+
}
269+
270+
server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.display), NULL);
271+
if (server.backend == NULL) {
272+
wlr_log(WLR_ERROR, "Cannot create backend");
273+
return 1;
274+
}
275+
276+
server.renderer = wlr_renderer_autocreate(server.backend);
277+
if (server.renderer == NULL) {
278+
wlr_log(WLR_ERROR, "Cannot create renderer");
279+
return 1;
280+
}
281+
282+
wlr_renderer_init_wl_display(server.renderer, server.display);
283+
284+
server.allocator = wlr_allocator_autocreate(server.backend, server.renderer);
285+
if (server.allocator == NULL) {
286+
wlr_log(WLR_ERROR, "Cannot create allocator");
287+
return 1;
288+
}
289+
290+
server.compositor = wlr_compositor_create(server.display, 5, server.renderer);
291+
server.subcompositor = wlr_subcompositor_create(server.display);
292+
wlr_data_device_manager_create(server.display);
293+
294+
server.output_layout = wlr_output_layout_create();
295+
server.scene = wlr_scene_create();
296+
297+
server.new_output.notify = server_new_output;
298+
wl_signal_add(&server.backend->events.new_output, &server.new_output);
299+
300+
server.xdg_shell = wlr_xdg_shell_create(server.display, 3);
301+
server.new_xdg_surface.notify = server_new_xdg_surface;
302+
wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface);
303+
304+
server.seat = wlr_seat_create(server.display, "seat0");
305+
server.request_set_cursor.notify = request_set_cursor;
306+
wl_signal_add(&server.seat->events.request_set_cursor, &server.request_set_cursor);
307+
server.request_set_selection.notify = request_set_selection;
308+
wl_signal_add(&server.seat->events.request_set_selection, &server.request_set_selection);
309+
310+
server.new_input.notify = server_new_input;
311+
wl_signal_add(&server.backend->events.new_input, &server.new_input);
312+
313+
const char *socket = wl_display_add_socket_auto(server.display);
314+
if (socket == NULL) {
315+
wlr_log(WLR_ERROR, "Cannot add socket");
316+
return 1;
317+
}
318+
319+
if (!wlr_backend_start(server.backend)) {
320+
wlr_log(WLR_ERROR, "Cannot start backend");
321+
return 1;
322+
}
323+
324+
setenv("WAYLAND_DISPLAY", socket, true);
325+
326+
server.child_pid = fork();
327+
if (server.child_pid == 0) {
328+
execvp(argv[1], argv + 1);
329+
perror("execvp");
330+
exit(1);
331+
} else if (server.child_pid < 0) {
332+
wlr_log(WLR_ERROR, "Cannot fork");
333+
return 1;
334+
}
335+
336+
wlr_log(WLR_INFO, "Running on WAYLAND_DISPLAY=%s", socket);
337+
wl_display_run(server.display);
338+
339+
if (server.child_pid > 0) {
340+
kill(server.child_pid, SIGTERM);
341+
waitpid(server.child_pid, NULL, 0);
342+
}
343+
344+
wl_display_destroy_clients(server.display);
345+
wl_display_destroy(server.display);
346+
wlr_scene_output_destroy(server.scene_output);
347+
wlr_output_layout_destroy(server.output_layout);
348+
wlr_allocator_destroy(server.allocator);
349+
wlr_renderer_destroy(server.renderer);
350+
wlr_backend_destroy(server.backend);
351+
352+
return 0;
353+
}

0 commit comments

Comments
 (0)