-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathclipscreen.c
214 lines (190 loc) · 6.4 KB
/
clipscreen.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/shape.h>
#include <cairo-xlib.h>
#include <cairo.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* @brief Draws a green rectangle on the given Cairo context.
*
* @param cr The Cairo context to draw on.
* @param w The width of the rectangle.
* @param h The height of the rectangle.
*/
void draw_rectangle(cairo_t *cr, int w, int h) {
cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5);
cairo_rectangle(cr, 0, 0, w, h);
cairo_set_line_width(cr, 10.0);
cairo_stroke(cr);
}
/**
* @brief Removes the virtual monitor from the display.
*
* Deletes the virtual monitor named "clipscreen" if it exists.
*
* @param display The display connection.
* @param root The root window.
*/
void remove_monitor(Display *display, Window root) {
int numMonitors;
XRRMonitorInfo *monitors = XRRGetMonitors(display, root, True, &numMonitors);
for (int i = 0; i < numMonitors; ++i) {
if (strcmp(XGetAtomName(display, monitors[i].name), "clipscreen") == 0) {
XRRDeleteMonitor(display, root, monitors[i].name);
printf("Removed virtual monitor\n");
break;
}
}
XFree(monitors);
}
/**
* @brief Adds a virtual monitor to the display.
*
* Creates a virtual monitor with the specified dimensions and position.
*
* The created monitor will be 10px smaller than specified to not overlap with the window border.
*
* @param d The display connection.
* @param root The root window.
* @param w The width of the monitor.
* @param h The height of the monitor.
* @param x The x-coordinate of the monitor.
* @param y The y-coordinate of the monitor.
*/
void add_monitor(Display *d, Window root, int w, int h, int x, int y) {
// remove any leftover virtual monitor
remove_monitor(d, root);
RROutput primary_output = XRRGetOutputPrimary(d, root);
// Create virtual monitor (equivalent to xrandr --setmonitor)
XRRMonitorInfo monitor;
monitor.name = XInternAtom(d, "clipscreen", False);
monitor.x = x + 5;
monitor.y = y + 5;
monitor.width = w - 10;
monitor.height = h - 10;
monitor.mwidth = w - 10; // Aspect ratio 1/1
monitor.mheight = h - 10; // Aspect ratio 1/1
monitor.noutput = 0; // Output 0 is none
monitor.outputs = &primary_output;
XRRSetMonitor(d, root, &monitor);
printf("Added virtual monitor\n");
}
/**
* @brief Creates an overlay window on the display.
*
* Sets up an overlay window with the specified dimensions and position.
*
* @param d The display connection.
* @param root The root window.
* @param vinfo The X visuals
* @param w The width of the overlay.
* @param h The height of the overlay.
* @param x The x-coordinate of the overlay.
* @param y The y-coordinate of the overlay.
* @return Window The created overlay window.
*/
Window create_overlay_window(Display *d, Window root, XVisualInfo vinfo, int w, int h, int x, int y) {
XSetWindowAttributes attrs;
attrs.override_redirect = true;
attrs.colormap = XCreateColormap(d, root, vinfo.visual, AllocNone);
attrs.background_pixel = 0;
attrs.border_pixel = 0;
Window overlay = XCreateWindow(d, root, x, y, w, h, 0, vinfo.depth, InputOutput, vinfo.visual,
CWOverrideRedirect | CWColormap | CWBackPixel | CWBorderPixel, &attrs);
XRectangle rect;
XserverRegion region = XFixesCreateRegion(d, &rect, 0);
XFixesSetWindowShapeRegion(d, overlay, ShapeInput, 0, 0, region);
XFixesDestroyRegion(d, region);
XMapWindow(d, overlay);
return overlay;
}
/**
* @brief Parses the geometry from the command-line arguments.
*
* Sets the width, height, x, and y accordingly and ensures a minimum size of 100x100.
*
* Exits on errors.
*
* @param argc The number of command-line arguments.
* @param argv The array of command-line arguments.
* @param w The width of the window.
* @param h The height of the window.
* @param x The x-coordinate of the window.
* @param y The y-coordinate of the window.
*/
void initGeometry(int argc, char *argv[], unsigned int *w, unsigned int *h, int *x, int *y) {
if (argc > 2 || argc <= 1) {
fprintf(stderr, "Usage: %s <width>x<height>+<x>+<y> (e.g. 800x600+100+100)\n", argv[0]);
exit(EXIT_FAILURE);
}
int ret = XParseGeometry(argv[1], x, y, w, h);
if (ret == 0) {
fprintf(stderr, "invalid geometry: %s (e.g. 800x600+100+100)\n", argv[1]);
exit(EXIT_FAILURE);
}
// ensure minimum size so that we can draw a border around something
if (*w < 100) {
fprintf(stderr, "Auto adjusted width\n");
*w = 100;
}
if (*h < 100) {
fprintf(stderr, "Auto adjusted height\n");
*h = 100;
}
}
/**
* @brief Main function for the clipscreen application.
*
* Sets up a virtual monitor and draws a rectangle around it. Waits for SIGINT to terminate.
*
* @param argc The number of command-line arguments.
* @param argv The array of command-line arguments.
* @return int Exit status.
*/
int main(int argc, char *argv[]) {
unsigned int w = 0;
unsigned int h = 0;
int x = 0;
int y = 0;
// parse geometry from arguments
initGeometry(argc, argv, &w, &h, &x, &y);
// set up X11
Display *d = XOpenDisplay(NULL);
Window root = DefaultRootWindow(d);
// add virtual monitor
add_monitor(d, root, w, h, x, y);
// Initialize Visuals and check for 32 bit color support
XVisualInfo vinfo;
if (!XMatchVisualInfo(d, DefaultScreen(d), 32, TrueColor, &vinfo)) {
printf("No visual found supporting 32 bit color, terminating\n");
exit(EXIT_FAILURE);
}
// create overlay border
Window overlay = create_overlay_window(d, root, vinfo, w, h, x, y);
cairo_surface_t *surf = cairo_xlib_surface_create(d, overlay, vinfo.visual, w, h);
cairo_t *cr = cairo_create(surf);
draw_rectangle(cr, w, h);
XFlush(d);
// wait for SIGINT or SIGTERM
printf("Press Ctrl-C to exit\n");
int sig;
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigprocmask(SIG_BLOCK, &sigset, NULL);
sigwait(&sigset, &sig);
// clean up
remove_monitor(d, root);
cairo_destroy(cr);
cairo_surface_destroy(surf);
XUnmapWindow(d, overlay);
XCloseDisplay(d);
return 0;
}