gimp but for windows: a wayland compositor
git clone https://github.com/m-col/wimp
Files | Refs | Readme

-rw-r--r-- src/output.c


      1 #include <math.h>
      2 #include <time.h>
      3 #include <pixman.h>
      4 #include <wlr/types/wlr_gamma_control_v1.h>
      5 #include <wlr/types/wlr_layer_shell_v1.h>
      6 #include <wlr/types/wlr_matrix.h>
      7 #include <wlr/types/wlr_output_damage.h>
      8 #include <wlr/types/wlr_output_layout.h>
      9 #include <wlr/types/wlr_output_management_v1.h>
     10 #include <wlr/types/wlr_screencopy_v1.h>
     11 #include <wlr/types/wlr_xdg_output_v1.h>
     12 
     13 #include "output.h"
     14 #include "types.h"
     15 
     16 
     17 struct render_data {
     18     struct wlr_output *output;
     19     struct wlr_renderer *renderer;
     20     struct wlr_surface *bordered;
     21     struct timespec *when;
     22     double zoom;
     23     bool is_focussed;
     24     int x;
     25     int y;
     26 };
     27 
     28 
     29 static void render_borders(
     30     struct render_data *rdata, int x, int y, int width, int height
     31 ) {
     32     double border_width = ceil(wimp.current_desk->border_width * rdata->zoom);
     33     int corner = CORNER * rdata->zoom;
     34 
     35     struct wlr_output *output = rdata->output;
     36     float *colour = rdata->is_focussed ?
     37 	wimp.current_desk->corner_focus : wimp.current_desk->corner_normal;
     38 
     39     // edges including corners
     40     struct wlr_box border = {
     41 	.x = x - border_width,
     42 	.y = y - border_width,
     43 	.width = width + border_width * 2,
     44 	.height = border_width,
     45     };
     46     wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // N
     47     border.y = y + height;
     48     wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // S
     49     border.y = y - border_width;
     50     border.width = border_width;
     51     border.height = height + border_width * 2;
     52     wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // W
     53     border.x = x + width;
     54     wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // E
     55 
     56     // edges excluding corners
     57     colour = rdata->is_focussed ?  wimp.current_desk->border_focus : wimp.current_desk->border_normal;
     58     border.y += corner;
     59     border.height -= corner * 2;
     60     if (border.height > 0) {
     61 	wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // E
     62 	border.x = x - border_width;
     63 	wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // W
     64     } else {
     65 	border.x = x - border_width;
     66     }
     67     border.y = y - border_width;
     68     border.x += corner;
     69     border.height = border_width;
     70     border.width = width + border_width * 2 - corner * 2;
     71     if (border.width > 0) {
     72 	wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // S
     73 	border.y = y + height;
     74 	wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // N
     75     }
     76 }
     77 
     78 
     79 static void render_surface(
     80     struct wlr_surface *surface, int sx, int sy, void *data
     81 ) {
     82     struct render_data *rdata = data;
     83     struct wlr_output *output = rdata->output;
     84 
     85     struct wlr_texture *texture = wlr_surface_get_texture(surface);
     86     if (texture == NULL) {
     87 	return;
     88     }
     89 
     90     int x = (rdata->x + sx) * output->scale * rdata->zoom;
     91     int y = (rdata->y + sy) * output->scale * rdata->zoom;
     92     int width = surface->current.width * output->scale * rdata->zoom;
     93     int height = surface->current.height * output->scale * rdata->zoom;
     94 
     95     if (rdata->bordered == surface && wimp.current_desk->border_width > 0) {
     96 	render_borders(rdata, x, y, width, height);
     97     }
     98 
     99     struct wlr_box box = {
    100 	.x = x,
    101 	.y = y,
    102 	.width = width,
    103 	.height = height,
    104     };
    105 
    106     float matrix[9];
    107     enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
    108     wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
    109     wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1);
    110 
    111     wlr_surface_send_frame_done(surface, rdata->when);
    112 }
    113 
    114 
    115 static void on_frame(struct wl_listener *listener, void *data) {
    116     struct output *output = wl_container_of(listener, output, frame_listener);
    117 
    118     bool needs_frame;
    119     pixman_region32_t damage;
    120     pixman_region32_init(&damage);
    121 
    122     if (!wlr_output_damage_attach_render(output->wlr_output_damage, &needs_frame, &damage)) {
    123 	goto finish;
    124     }
    125 
    126     if (!needs_frame) {
    127 	wlr_output_rollback(output->wlr_output);
    128 	goto finish;
    129     }
    130 
    131     struct wlr_renderer *renderer = wimp.renderer;
    132     struct desk *desk = wimp.current_desk;
    133     struct timespec now;
    134     clock_gettime(CLOCK_MONOTONIC, &now);
    135 
    136     int width, height;
    137     double zoom = desk->zoom;
    138     wlr_output_effective_resolution(output->wlr_output, &width, &height);
    139     wlr_renderer_begin(renderer, width, height);
    140     wlr_renderer_clear(renderer, desk->background);
    141 
    142     double ox, oy;
    143     struct wlr_output_layout_output *ol;
    144     wl_list_for_each(ol, &wimp.output_layout->outputs, link) {
    145 	if (ol->output == output->wlr_output) {
    146 	    ox = (double)ol->x;
    147 	    oy = (double)ol->y;
    148 	    break;
    149 	}
    150     }
    151 
    152     // paint wallpaper
    153     struct wallpaper *wallpaper = desk->wallpaper;
    154     if (wallpaper != NULL) {
    155 	int wh = wallpaper->height;
    156 	int ww = wallpaper->width;
    157 	float mat[9];
    158 	memcpy(mat, output->wlr_output->transform_matrix, 9 * sizeof(float));
    159 	wlr_matrix_scale(mat, zoom, zoom);
    160 	for (int x = ((int)desk->panned_x % ww) - ww; x < width / zoom; x += ww) {
    161 	    for (int y = ((int)desk->panned_y % wh) - wh; y < height / zoom; y += wh) {
    162 		wlr_render_texture(renderer, wallpaper->texture, mat, x, y, 1.0f);
    163 	    }
    164 	}
    165     }
    166 
    167     struct render_data rdata = {
    168 	.output = output->wlr_output,
    169 	.renderer = renderer,
    170 	.bordered = NULL,
    171 	.when = &now,
    172 	.zoom = 1,
    173 	.is_focussed = false,
    174 	.x = 0,
    175 	.y = 0,
    176     };
    177 
    178     // paint background and bottom layers
    179     struct layer_view *lview;
    180     wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], link) {
    181 	wlr_surface_for_each_surface(
    182 	    lview->surface->surface, render_surface, &rdata
    183 	);
    184     }
    185     wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], link) {
    186 	wlr_surface_for_each_surface(
    187 	    lview->surface->surface, render_surface, &rdata
    188 	);
    189     }
    190 
    191     rdata.zoom = zoom;
    192 
    193     // paint clients
    194     struct view *view;
    195     struct wlr_surface *focussed = wimp.seat->keyboard_state.focused_surface;
    196     int border_width = wimp.current_desk->border_width;
    197     wl_list_for_each_reverse(view, &desk->views, link) {
    198 	rdata.x = view->x - ox / zoom;
    199 	rdata.y = view->y - oy / zoom;
    200 	if (
    201 		(rdata.x + view->surface->geometry.width + border_width < 0) ||
    202 		(rdata.y + view->surface->geometry.height + border_width < 0) ||
    203 		(rdata.x - border_width > width / zoom) || (rdata.y - border_width > height / zoom)
    204 	) {
    205 	    continue;
    206 	}
    207 	rdata.is_focussed = (view->surface->surface == focussed);
    208 	rdata.bordered = view->surface->surface;
    209 	wlr_xdg_surface_for_each_surface(view->surface, render_surface, &rdata);
    210     }
    211 
    212     rdata.zoom = 1;
    213 
    214     struct scratchpad *scratchpad;
    215     wl_list_for_each(scratchpad, &wimp.scratchpads, link) {
    216 	if (scratchpad->is_mapped) {
    217 	    view = scratchpad->view;
    218 	    rdata.x = view->x;
    219 	    rdata.y = view->y;
    220 	    rdata.is_focussed = (view->surface->surface == focussed);
    221 	    rdata.bordered = view->surface->surface;
    222 	    wlr_xdg_surface_for_each_surface(view->surface, render_surface, &rdata);
    223 	}
    224     }
    225 
    226     rdata.bordered = NULL;
    227     rdata.is_focussed = false;
    228 
    229     // paint top and overlay layers
    230     wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_TOP], link) {
    231 	rdata.x = lview->geo.x;
    232 	rdata.y = lview->geo.y;
    233 	wlr_surface_for_each_surface(
    234 	    lview->surface->surface, render_surface, &rdata
    235 	);
    236     }
    237     wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) {
    238 	rdata.x = lview->geo.x;
    239 	rdata.y = lview->geo.y;
    240 	wlr_surface_for_each_surface(
    241 	    lview->surface->surface, render_surface, &rdata
    242 	);
    243     }
    244 
    245     // paint mark indicator
    246     if (wimp.mark_waiting) {
    247 	struct wlr_box indicator = wimp.mark_indicator.box;
    248 	indicator.y = height - indicator.height;
    249 	wlr_render_rect(
    250 	    renderer, &indicator, wimp.mark_indicator.colour,
    251 	    output->wlr_output->transform_matrix
    252 	);
    253     }
    254 
    255     // paint snap box
    256     if (wimp.can_snap) {
    257 	wlr_render_rect(
    258 	    renderer, &wimp.snap_geobox, wimp.snapbox_colour,
    259 	    output->wlr_output->transform_matrix
    260 	);
    261     }
    262 
    263     wlr_output_render_software_cursors(output->wlr_output, NULL);  // no-op with HW cursors
    264     wlr_renderer_end(renderer);
    265     wlr_output_commit(output->wlr_output);
    266 
    267 finish:
    268     pixman_region32_fini(&damage);
    269 }
    270 
    271 
    272 void damage_box(struct wlr_box *geo, bool add_borders) {
    273     struct output *output;
    274 
    275     if (add_borders) {
    276 	struct wlr_box damaged;
    277 	int border_width = wimp.current_desk->border_width;
    278 	memcpy(&damaged, geo, sizeof(struct wlr_box));
    279 	damaged.x -= border_width;
    280 	damaged.y -= border_width;
    281 	damaged.width += border_width * 2;
    282 	damaged.height += border_width * 2;
    283 	wl_list_for_each(output, &wimp.outputs, link) {
    284 	    wlr_output_damage_add_box(output->wlr_output_damage, &damaged);
    285 	}
    286     } else {
    287 	wl_list_for_each(output, &wimp.outputs, link) {
    288 	    wlr_output_damage_add_box(output->wlr_output_damage, geo);
    289 	}
    290     }
    291 }
    292 
    293 
    294 void damage_by_view(struct view *view, bool with_borders) {
    295     double zoom = wimp.current_desk->zoom;
    296 
    297     struct wlr_box geo = {
    298 	.x = view->x,
    299 	.y = view->y,
    300 	.width = view->width * zoom,
    301 	.height = view->height * zoom,
    302     };
    303 
    304     damage_box(&geo, with_borders);
    305 }
    306 
    307 
    308 void damage_by_lview(struct layer_view *lview) {
    309     damage_box(&lview->geo, false);
    310 }
    311 
    312 
    313 void damage_all_outputs() {
    314     struct output *output;
    315     wl_list_for_each(output, &wimp.outputs, link) {
    316 	wlr_output_damage_add_whole(output->wlr_output_damage);
    317     }
    318 }
    319 
    320 
    321 void damage_all_views() {
    322     struct view *view;
    323     wl_list_for_each(view, &wimp.current_desk->views, link) {
    324 	damage_by_view(view, true);
    325     }
    326 }
    327 
    328 
    329 void damage_mark_indicator() {
    330     struct output *output;
    331     struct wlr_box indicator = wimp.mark_indicator.box;
    332     int width, height;
    333 
    334     wl_list_for_each(output, &wimp.outputs, link) {
    335 	wlr_output_effective_resolution(output->wlr_output, &width, &height);
    336 	struct wlr_box geo = {
    337 	    .x = 0,
    338 	    .y = height - indicator.height,
    339 	    .width = indicator.width,
    340 	    .height = indicator.height,
    341 	};
    342 	wlr_output_damage_add_box(output->wlr_output_damage, &geo);
    343     }
    344 }
    345 
    346 
    347 static void on_destroy(struct wl_listener *listener, void *data) {
    348     struct output *output = wl_container_of(listener, output, destroy_listener);
    349 
    350     struct layer_view *lview, *tlview;
    351     for (int i = 0; i < 4; i++ ) {
    352         wl_list_for_each_safe(lview, tlview, &output->layer_views[i], link) {
    353             wl_list_remove(&lview->link);
    354             wl_list_remove(&lview->map_listener.link);
    355             wl_list_remove(&lview->unmap_listener.link);
    356             wl_list_remove(&lview->destroy_listener.link);
    357             free(lview);
    358         };
    359     }
    360 
    361     wl_list_remove(&output->frame_listener.link);
    362     wl_list_remove(&output->destroy_listener.link);
    363     wl_list_remove(&output->link);
    364     free(output);
    365 }
    366 
    367 
    368 static void on_new_output(struct wl_listener *listener, void *data) {
    369     struct wlr_output *wlr_output = data;
    370 
    371     if (!wl_list_empty(&wlr_output->modes)) {
    372 	struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
    373 	wlr_output_set_mode(wlr_output, mode);
    374 	wlr_output_enable(wlr_output, true);
    375 	if (!wlr_output_commit(wlr_output)) {
    376 	    return;
    377 	}
    378     }
    379 
    380     struct output *output = calloc(1, sizeof(struct output));
    381     output->wlr_output = wlr_output;
    382     wlr_output->data = output;
    383     output->wlr_output_damage = wlr_output_damage_create(wlr_output);
    384 
    385     output->frame_listener.notify = on_frame;
    386     output->destroy_listener.notify = on_destroy;
    387     wl_signal_add(&output->wlr_output_damage->events.frame, &output->frame_listener);
    388     wl_signal_add(&wlr_output->events.destroy, &output->destroy_listener);
    389 
    390     wl_list_insert(&wimp.outputs, &output->link);
    391     wlr_output_layout_add_auto(wimp.output_layout, wlr_output);
    392 
    393     wl_list_init(&output->layer_views[0]);
    394     wl_list_init(&output->layer_views[1]);
    395     wl_list_init(&output->layer_views[2]);
    396     wl_list_init(&output->layer_views[3]);
    397 }
    398 
    399 
    400 static void output_manager_reconfigure(
    401     struct wlr_output_configuration_v1 *config, bool commit
    402 ) {
    403     struct wlr_output_configuration_head_v1 *config_head;
    404     bool ok = 1;
    405 
    406     wl_list_for_each(config_head, &config->heads, link) {
    407         struct wlr_output *wlr_output = config_head->state.output;
    408 
    409         wlr_output_enable(wlr_output, config_head->state.enabled);
    410         if (config_head->state.enabled) {
    411             if (config_head->state.mode) {
    412                 wlr_output_set_mode(wlr_output, config_head->state.mode);
    413 	    } else {
    414                 wlr_output_set_custom_mode(
    415 		    wlr_output,
    416 		    config_head->state.custom_mode.width,
    417 		    config_head->state.custom_mode.height,
    418 		    config_head->state.custom_mode.refresh
    419 		);
    420 	    }
    421 
    422             wlr_output_layout_move(
    423 		wimp.output_layout, wlr_output, config_head->state.x, config_head->state.y
    424 	    );
    425             wlr_output_set_transform(wlr_output, config_head->state.transform);
    426             wlr_output_set_scale(wlr_output, config_head->state.scale);
    427         }
    428 
    429         ok = wlr_output_test(wlr_output);
    430         if (!ok) {
    431             break;
    432 	}
    433     }
    434 
    435     wl_list_for_each(config_head, &config->heads, link) {
    436         if (ok && commit) {
    437             wlr_output_commit(config_head->state.output);
    438 	} else {
    439             wlr_output_rollback(config_head->state.output);
    440 	}
    441     }
    442 
    443     if (ok) {
    444         wlr_output_configuration_v1_send_succeeded(config);
    445     } else {
    446         wlr_output_configuration_v1_send_failed(config);
    447     }
    448     wlr_output_configuration_v1_destroy(config);
    449 }
    450 
    451 
    452 static void on_output_manager_apply(struct wl_listener *listener, void *data) {
    453     output_manager_reconfigure(data, true);
    454 }
    455 
    456 
    457 static void on_output_manager_test(struct wl_listener *listener, void *data) {
    458     output_manager_reconfigure(data, false);
    459 }
    460 
    461 
    462 static void on_output_layout_change(struct wl_listener *listener, void *data) {
    463     struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create();
    464 
    465     struct output *output;
    466     wl_list_for_each(output, &wimp.outputs, link) {
    467 	struct wlr_output_configuration_head_v1 *config_head =
    468 	    wlr_output_configuration_head_v1_create(config, output->wlr_output);
    469 
    470 	struct wlr_box *box = wlr_output_layout_get_box(wimp.output_layout, output->wlr_output);
    471 	config_head->state.x = box->x;
    472 	config_head->state.y = box->y;
    473 	config_head->state.enabled = output->wlr_output->enabled;
    474 	config_head->state.mode = output->wlr_output->current_mode;
    475     }
    476 
    477     wlr_output_manager_v1_set_configuration(wimp.output_manager, config);
    478 }
    479 
    480 
    481 void set_up_outputs() {
    482     wimp.output_layout = wlr_output_layout_create();
    483     wimp.output_layout_change_listener.notify = on_output_layout_change;
    484     wl_signal_add(&wimp.output_layout->events.change, &wimp.output_layout_change_listener);
    485 
    486     wl_list_init(&wimp.outputs);
    487     wimp.new_output_listener.notify = on_new_output;
    488     wl_signal_add(&wimp.backend->events.new_output, &wimp.new_output_listener);
    489 
    490     wlr_screencopy_manager_v1_create(wimp.display);
    491     wlr_xdg_output_manager_v1_create(wimp.display, wimp.output_layout);
    492 
    493     wimp.output_manager = wlr_output_manager_v1_create(wimp.display);
    494     wimp.output_manager_apply_listener.notify = on_output_manager_apply;
    495     wimp.output_manager_test_listener.notify = on_output_manager_test;
    496     wl_signal_add(&wimp.output_manager->events.apply, &wimp.output_manager_apply_listener);
    497     wl_signal_add(&wimp.output_manager->events.test, &wimp.output_manager_test_listener);
    498 
    499     wlr_gamma_control_manager_v1_create(wimp.display);
    500 }
    501