monitor.c (6410B)
1 /* 2 * kbgwm, a sucklessy floating window manager 3 * Copyright (c) 2020-2021, Kebigon <git@kebigon.xyz> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "monitor.h" 19 20 #include <stdlib.h> 21 22 #include "list.h" 23 #include "log.h" 24 #include "xcbutils.h" 25 26 extern xcb_connection_t *c; 27 extern xcb_screen_t *screen; 28 extern const uint_least8_t workspaces_length; 29 extern struct item *workspaces[]; 30 31 int_least16_t randr_base = -1; 32 struct item *monitors; 33 34 void monitor_add(xcb_randr_output_t, int16_t, int16_t, uint16_t, uint16_t); 35 struct item *monitor_find_by_id(const xcb_randr_output_t); 36 37 void setup_monitors() 38 { 39 const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, &xcb_randr_id); 40 if (!extension->present) 41 { 42 LOG_WARN("Unable to retrieve the RANDR extension"); 43 return; 44 } 45 46 monitors_refresh_config(); 47 48 randr_base = extension->first_event; 49 xcb_randr_select_input( 50 c, screen->root, 51 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | 52 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); 53 } 54 55 void monitor_add(xcb_randr_output_t id, int16_t x, int16_t y, uint16_t width, uint16_t height) 56 { 57 LOG_DEBUG_VA("monitor_add: id=%d, x=%d, y=%d, width=%d, height=%d", id, x, y, width, height); 58 59 struct monitor *monitor = emalloc(sizeof(struct monitor)); 60 monitor->id = id; 61 monitor->x = x; 62 monitor->y = y; 63 monitor->width = width; 64 monitor->height = height; 65 66 list_add(&monitors, monitor); 67 } 68 69 void monitor_update(struct monitor *monitor, xcb_randr_get_crtc_info_reply_t *crtc) 70 { 71 if (crtc->x != monitor->x) 72 monitor->x = crtc->x; 73 if (crtc->y != monitor->y) 74 monitor->y = crtc->y; 75 if (crtc->width != monitor->width) 76 monitor->width = crtc->width; 77 if (crtc->height != monitor->height) 78 monitor->height = crtc->height; 79 80 // TODO: rearrange clients 81 } 82 83 void monitor_delete(struct item *mon_item) 84 { 85 // Replace the deleted by the next one, or the first one 86 struct monitor *new_mon = (mon_item->next != NULL ? mon_item->next : monitors)->data; 87 struct monitor *monitor = list_remove(&monitors, mon_item); 88 89 struct client *client; 90 struct item *cli_item; 91 92 // move clients to another monitor 93 for (uint_fast8_t workspace = 0; workspace != workspaces_length; workspace++) 94 { 95 for (cli_item = workspaces[workspace]; cli_item != NULL; cli_item = cli_item->next) 96 { 97 client = cli_item->data; 98 99 // Not the removed monitor 100 if (client->monitor->id != monitor->id) 101 continue; 102 103 client->monitor = new_mon; 104 client->x = new_mon->x; 105 client->y = new_mon->y; 106 107 // Sanitize 108 client_sanitize_position(client); 109 client_sanitize_dimensions(client); 110 } 111 } 112 } 113 114 void monitors_refresh_config() 115 { 116 const xcb_randr_get_screen_resources_current_cookie_t cookie = 117 xcb_randr_get_screen_resources_current(c, screen->root); 118 xcb_randr_get_screen_resources_current_reply_t *res = 119 xcb_randr_get_screen_resources_current_reply(c, cookie, NULL); 120 if (NULL == res) 121 { 122 LOG_WARN("Unable to retrieve the RANDR screen resources"); 123 return; 124 } 125 126 int32_t len = xcb_randr_get_screen_resources_current_outputs_length(res); 127 xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(res); 128 129 int_fast32_t i; 130 131 // Request information for all outputs. 132 xcb_randr_get_output_info_cookie_t ocookie[len]; 133 for (i = 0; i != len; i++) 134 { 135 ocookie[i] = xcb_randr_get_output_info(c, outputs[i], res->config_timestamp); 136 } 137 138 xcb_randr_get_output_info_reply_t *info; 139 xcb_randr_get_crtc_info_reply_t *crtc; 140 struct item *item; 141 142 // Process information replies 143 for (i = 0; i != len; i++) 144 { 145 info = xcb_randr_get_output_info_reply(c, ocookie[i], NULL); 146 if (info == NULL) 147 continue; 148 149 // Disabled / disconnected output 150 if (info->crtc == XCB_NONE || info->connection == XCB_RANDR_CONNECTION_DISCONNECTED) 151 { 152 // Already registered monitor -> delete it 153 if ((item = monitor_find_by_id(outputs[i])) != NULL) 154 { 155 monitor_delete(item); 156 } 157 158 continue; 159 } 160 161 crtc = xcb_randr_get_crtc_info_reply( 162 c, xcb_randr_get_crtc_info(c, info->crtc, res->config_timestamp), NULL); 163 164 // CRTC info not found -> skipping 165 if (crtc == NULL) 166 continue; 167 168 // Already registered monitor -> update it 169 if ((item = monitor_find_by_id(outputs[i])) != NULL) 170 { 171 monitor_update(item->data, crtc); 172 } 173 // New monitor -> add it to the list 174 else 175 { 176 monitor_add(outputs[i], crtc->x, crtc->y, crtc->width, crtc->height); 177 } 178 179 free(crtc); 180 free(info); 181 } 182 183 free(res); 184 } 185 186 bool monitor_contains(struct monitor *monitor, uint16_t x, uint16_t y) 187 { 188 return monitor->x <= x && x <= monitor->x + monitor->width && // 189 monitor->y <= y && y <= monitor->y + monitor->height; 190 } 191 192 // Find a monitor from its id 193 struct item *monitor_find_by_id(const xcb_randr_output_t id) 194 { 195 for (struct item *item = monitors; item != NULL; item = item->next) 196 if (((struct monitor *)item->data)->id == id) 197 return item; 198 199 return NULL; 200 } 201 202 struct item *monitor_find_by_position(const uint16_t x, const uint16_t y) 203 { 204 for (struct item *item = monitors; item != NULL; item = item->next) 205 if (monitor_contains(item->data, x, y)) 206 return item; 207 208 return NULL; 209 }