diff --git a/examples/meson.build b/examples/meson.build index 1ce9c342..e2c0189c 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -7,6 +7,7 @@ executable('rotation', 'rotation.c', dependencies: wlroots, link_with: lib_share executable('pointer', 'pointer.c', dependencies: wlroots, link_with: lib_shared) executable('touch', 'touch.c', dependencies: wlroots, link_with: lib_shared) executable('tablet', 'tablet.c', dependencies: wlroots, link_with: lib_shared) +executable('output-layout', 'output-layout.c', dependencies: wlroots, link_with: lib_shared) compositor_src = [ 'compositor/main.c', diff --git a/examples/output-layout.c b/examples/output-layout.c new file mode 100644 index 00000000..f0e2d2d4 --- /dev/null +++ b/examples/output-layout.c @@ -0,0 +1,276 @@ +#define _POSIX_C_SOURCE 199309L +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shared.h" +#include "cat.h" + +struct sample_state { + struct wl_list config; + struct wlr_renderer *renderer; + struct wlr_texture *cat_texture; + struct wlr_output_layout *layout; + float x_offs, y_offs; + float x_vel, y_vel; + struct wlr_output *main_output; +}; + +struct output_config { + char *name; + enum wl_output_transform transform; + int x, y; + struct wl_list link; +}; + +static void handle_output_frame(struct output_state *output, struct timespec *ts) { + struct compositor_state *state = output->compositor; + struct sample_state *sample = state->data; + struct wlr_output *wlr_output = output->output; + + int32_t width, height; + wlr_output_effective_resolution(wlr_output, &width, &height); + + wlr_output_make_current(wlr_output); + wlr_renderer_begin(sample->renderer, wlr_output); + + float matrix[16]; + + // transform global coordinates to local coordinates + int local_x = sample->x_offs; + int local_y = sample->y_offs; + + wlr_output_layout_output_coords(sample->layout, output->output, &local_x, + &local_y); + + wlr_texture_get_matrix(sample->cat_texture, &matrix, + &wlr_output->transform_matrix, local_x, local_y); + wlr_render_with_matrix(sample->renderer, + sample->cat_texture, &matrix); + + wlr_renderer_end(sample->renderer); + wlr_output_swap_buffers(wlr_output); + + if (output->output == sample->main_output) { + long ms = (ts->tv_sec - output->last_frame.tv_sec) * 1000 + + (ts->tv_nsec - output->last_frame.tv_nsec) / 1000000; + // how many seconds have passed since the last frame + float seconds = ms / 1000.0f; + + // check for collisions and bounce + bool ur_collision = !wlr_output_layout_output_at(sample->layout, + sample->x_offs + 128, sample->y_offs); + bool ul_collision = !wlr_output_layout_output_at(sample->layout, + sample->x_offs, sample->y_offs); + bool ll_collision = !wlr_output_layout_output_at(sample->layout, + sample->x_offs, sample->y_offs + 128); + bool lr_collision = !wlr_output_layout_output_at(sample->layout, + sample->x_offs + 128, sample->y_offs + 128); + bool has_double_collision = false; + + if ((ur_collision && ul_collision) || (lr_collision && ll_collision)) { + sample->y_vel *= -1; + has_double_collision = true; + } + + if ((ll_collision && ul_collision) || (ur_collision && lr_collision)) { + sample->x_vel *= -1; + has_double_collision = true; + } + + if (!has_double_collision && + (ur_collision || ul_collision || lr_collision || ll_collision)) { + sample->x_vel *= -1; + sample->y_vel *= -1; + } + + sample->x_offs += sample->x_vel * seconds; + sample->y_offs += sample->y_vel * seconds; + } +} + +static void handle_output_add(struct output_state *output) { + struct sample_state *sample = output->compositor->data; + + struct output_config *conf; + wl_list_for_each(conf, &sample->config, link) { + if (strcmp(conf->name, output->output->name) == 0) { + wlr_output_layout_add(sample->layout, output->output, + conf->x, conf->y); + wlr_output_transform(output->output, conf->transform); + + if (!sample->main_output) { + sample->main_output = output->output; + sample->x_offs = conf->x + 20; + sample->y_offs = conf->y + 20; + sample->x_vel = 500; + sample->y_vel = 500; + } + wlr_log(L_DEBUG, "Adding output to layout: %s", output->output->name); + } + + } +} + +static void update_velocities(struct compositor_state *state, + float x_diff, float y_diff) { + struct sample_state *sample = state->data; + sample->x_vel += x_diff; + sample->y_vel += y_diff; +} + +static void handle_keyboard_key(struct keyboard_state *kbstate, + xkb_keysym_t sym, enum wlr_key_state key_state) { + // NOTE: It may be better to simply refer to our key state during each frame + // and make this change in pixels/sec^2 + // Also, key repeat + int delta = 75; + if (key_state == WLR_KEY_PRESSED) { + switch (sym) { + case XKB_KEY_Left: + update_velocities(kbstate->compositor, -delta, 0); + break; + case XKB_KEY_Right: + update_velocities(kbstate->compositor, delta, 0); + break; + case XKB_KEY_Up: + update_velocities(kbstate->compositor, 0, -delta); + break; + case XKB_KEY_Down: + update_velocities(kbstate->compositor, 0, delta); + break; + } + } +} + +static void usage(const char *name, int ret) { + fprintf(stderr, + "usage: %s [-d [-r | -f]]*\n" + "\n" + " -o The name of the DRM display. e.g. DVI-I-1.\n" + " -r The rotation counter clockwise. Valid values are 90, 180, 270.\n" + " -x The X-axis coordinate position of this output in the layout.\n" + " -y The Y-axis coordinate position of this output in the layout.\n" + " -f Flip the output along the vertical axis.\n", name); + + exit(ret); +} + +static void parse_args(int argc, char *argv[], struct wl_list *config) { + struct output_config *oc = NULL; + + int c; + while ((c = getopt(argc, argv, "o:r:x:y:fh")) != -1) { + switch (c) { + case 'o': + oc = calloc(1, sizeof(*oc)); + oc->name = optarg; + oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; + wl_list_insert(config, &oc->link); + break; + case 'r': + if (!oc) { + fprintf(stderr, "You must specify an output first\n"); + usage(argv[0], 1); + } + + if (oc->transform != WL_OUTPUT_TRANSFORM_NORMAL + && oc->transform != WL_OUTPUT_TRANSFORM_FLIPPED) { + fprintf(stderr, "Rotation for %s already specified\n", oc->name); + usage(argv[0], 1); + } + + if (strcmp(optarg, "90") == 0) { + oc->transform += WL_OUTPUT_TRANSFORM_90; + } else if (strcmp(optarg, "180") == 0) { + oc->transform += WL_OUTPUT_TRANSFORM_180; + } else if (strcmp(optarg, "270") == 0) { + oc->transform += WL_OUTPUT_TRANSFORM_270; + } else { + fprintf(stderr, "Invalid rotation '%s'\n", optarg); + usage(argv[0], 1); + } + break; + case 'x': + if (!oc) { + fprintf(stderr, "You must specify an output first\n"); + usage(argv[0], 1); + } + oc->x = strtol(optarg, NULL, 0); + break; + case 'y': + if (!oc) { + fprintf(stderr, "You must specify an output first\n"); + usage(argv[0], 1); + } + oc->y = strtol(optarg, NULL, 0); + break; + case 'f': + if (!oc) { + fprintf(stderr, "You must specify an output first\n"); + usage(argv[0], 1); + } + + if (oc->transform >= WL_OUTPUT_TRANSFORM_FLIPPED) { + fprintf(stderr, "Flip for %s already specified\n", oc->name); + usage(argv[0], 1); + } + + oc->transform += WL_OUTPUT_TRANSFORM_FLIPPED; + break; + case 'h': + case '?': + usage(argv[0], c != 'h'); + } + } +} + +int main(int argc, char *argv[]) { + struct sample_state state = {0}; + + state.layout = wlr_output_layout_init(); + + wl_list_init(&state.config); + parse_args(argc, argv, &state.config); + + struct compositor_state compositor = { 0 }; + compositor.data = &state; + compositor.output_add_cb = handle_output_add; + compositor.output_frame_cb = handle_output_frame; + compositor.keyboard_key_cb = handle_keyboard_key; + compositor_init(&compositor); + + state.renderer = wlr_gles2_renderer_init(compositor.backend); + state.cat_texture = wlr_render_texture_init(state.renderer); + wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888, + cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data); + + compositor_run(&compositor); + + wlr_texture_destroy(state.cat_texture); + wlr_renderer_destroy(state.renderer); + + wlr_output_layout_destroy(state.layout); + + struct output_config *ptr, *tmp; + wl_list_for_each_safe(ptr, tmp, &state.config, link) { + free(ptr); + } +} diff --git a/include/wlr/types/wlr_output_layout.h b/include/wlr/types/wlr_output_layout.h new file mode 100644 index 00000000..cd36482a --- /dev/null +++ b/include/wlr/types/wlr_output_layout.h @@ -0,0 +1,40 @@ +#ifndef _WLR_TYPES_OUTPUT_LAYOUT_H +#define _WLR_TYPES_OUTPUT_LAYOUT_H +#include +#include +#include + +struct wlr_output_layout { + struct wl_list outputs; +}; + +struct wlr_output_layout_output { + struct wlr_output *output; + int x, y; + struct wl_list link; +}; + +struct wlr_output_layout *wlr_output_layout_init(); + +void wlr_output_layout_destroy(struct wlr_output_layout *layout); + +struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout, + double x, double y); + +void wlr_output_layout_add(struct wlr_output_layout *layout, + struct wlr_output *output, int x, int y); + +void wlr_output_layout_move(struct wlr_output_layout *layout, + struct wlr_output *output, int x, int y); + +void wlr_output_layout_remove(struct wlr_output_layout *layout, + struct wlr_output *output); + +/** + * Given x and y as pointers to global coordinates, adjusts them to local output + * coordinates relative to the given reference output. + */ +void wlr_output_layout_output_coords(struct wlr_output_layout *layout, + struct wlr_output *reference, int *x, int *y); + +#endif diff --git a/types/meson.build b/types/meson.build index 61dd913c..bb313565 100644 --- a/types/meson.build +++ b/types/meson.build @@ -2,6 +2,7 @@ lib_wlr_types = static_library('wlr_types', files( 'wlr_input_device.c', 'wlr_keyboard.c', 'wlr_output.c', + 'wlr_output_layout.c', 'wlr_pointer.c', 'wlr_region.c', 'wlr_seat.c', diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c new file mode 100644 index 00000000..8b4256f9 --- /dev/null +++ b/types/wlr_output_layout.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +struct wlr_output_layout *wlr_output_layout_init() { + struct wlr_output_layout *layout = calloc(1, sizeof(struct wlr_output_layout)); + wl_list_init(&layout->outputs); + return layout; +} + +void wlr_output_layout_destroy(struct wlr_output_layout *layout) { + if (!layout) { + return; + } + + struct wlr_output_layout_output *_output, *temp = NULL; + wl_list_for_each_safe(_output, temp, &layout->outputs, link) { + wl_list_remove(&_output->link); + free(_output); + } + + free(layout); +} + +void wlr_output_layout_add(struct wlr_output_layout *layout, + struct wlr_output *output, int x, int y) { + struct wlr_output_layout_output *layout_output = calloc(1, sizeof(struct wlr_output_layout_output)); + layout_output->output = output; + layout_output->x = x; + layout_output->y = y; + wl_list_insert(&layout->outputs, &layout_output->link); +} + +static struct wlr_output_layout_output *wlr_output_layout_get( + struct wlr_output_layout *layout, struct wlr_output *reference) { + struct wlr_output_layout_output *ret = NULL; + struct wlr_output_layout_output *_output; + wl_list_for_each(_output, &layout->outputs, link) { + if (_output->output) { + ret = _output; + } + } + + return ret; + +} + +struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout, + double x, double y) { + struct wlr_output *ret = NULL; + struct wlr_output_layout_output *_output; + wl_list_for_each(_output, &layout->outputs, link) { + if (_output->output) { + int width, height; + wlr_output_effective_resolution(_output->output, &width, &height); + bool has_x = x >= _output->x && x <= _output->x + width; + bool has_y = y >= _output->y && y <= _output->y + height; + if (has_x && has_y) { + ret = _output->output; + break; + } + } + } + + return ret; +} + +void wlr_output_layout_move(struct wlr_output_layout *layout, + struct wlr_output *output, int x, int y) { + struct wlr_output_layout_output *layout_output = + wlr_output_layout_get(layout, output); + if (layout_output) { + layout_output->x = x; + layout_output->y = y; + } +} + +void wlr_output_layout_remove(struct wlr_output_layout *layout, + struct wlr_output *output) { + struct wlr_output_layout_output *layout_output = + wlr_output_layout_get(layout, output); + if (layout_output) { + wl_list_remove(&layout_output->link); + free(layout_output); + } +} + +void wlr_output_layout_output_coords(struct wlr_output_layout *layout, + struct wlr_output *reference, int *x, int *y) { + assert(layout && reference); + int src_x = *x; + int src_y = *y; + + struct wlr_output_layout_output *_output; + wl_list_for_each(_output, &layout->outputs, link) { + if (_output->output == reference) { + *x = src_x - _output->x; + *y = src_y - _output->y; + return; + } + } +}