Multimonitor support
This commit is contained in:
parent
04073fd407
commit
17ce92d9d1
2
Makefile
2
Makefile
@ -5,7 +5,7 @@ SRC=main.c
|
||||
# DEBUG=-g
|
||||
DEBUG=
|
||||
$(TARGET): main.c config.h
|
||||
gcc -Wall -Wextra -pedantic $(DEBUG) $(SRC) `pkg-config --cflags --libs cairo x11` -o $(TARGET) -lm
|
||||
gcc -Wall -Wextra -pedantic $(DEBUG) $(SRC) `pkg-config --cflags --libs cairo x11 xinerama` -o $(TARGET) -lm
|
||||
install:
|
||||
cp $(TARGET) $(DEST)
|
||||
uninstall:
|
||||
|
||||
267
main.c
267
main.c
@ -5,39 +5,39 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#include <cairo.h>
|
||||
#include <cairo-xlib.h>
|
||||
#include <signal.h>
|
||||
#include "config.h"
|
||||
|
||||
#define VERSION "0.100"
|
||||
|
||||
// --- Globals for Signal Handling ---
|
||||
volatile sig_atomic_t running = 1; // <--- Flag to control main loop
|
||||
volatile sig_atomic_t running = 1;
|
||||
|
||||
// --- Signal Handler Function ---
|
||||
void handle_signal(int signum) { // <--- Simple handler to stop the loop
|
||||
(void) signum;
|
||||
void handle_signal(int signum) {
|
||||
(void)signum; // Acknowledge parameter to prevent unused warning
|
||||
running = 0;
|
||||
}
|
||||
|
||||
// --- Struct Definitions ---
|
||||
typedef struct _rectangle {
|
||||
int pos_x;
|
||||
int pos_y;
|
||||
int width;
|
||||
int height;
|
||||
int color;
|
||||
|
||||
} rectangle;
|
||||
|
||||
|
||||
typedef struct _myTime {
|
||||
unsigned int hour;
|
||||
unsigned int minute;
|
||||
} myTime;
|
||||
} myTime;
|
||||
|
||||
myTime getTime(void){
|
||||
// --- Time Functions ---
|
||||
myTime getTime(void) {
|
||||
time_t raw_time;
|
||||
time(&raw_time);
|
||||
struct tm *currTime;
|
||||
@ -48,114 +48,144 @@ myTime getTime(void){
|
||||
return t;
|
||||
}
|
||||
|
||||
void print_current_time(void){
|
||||
myTime t=getTime();
|
||||
void print_current_time(void) {
|
||||
myTime t = getTime();
|
||||
printf("%02d:%02d\n", t.hour, t.minute);
|
||||
}
|
||||
|
||||
void getFibbTime(myTime t, int* res){
|
||||
// --- Fibonacci Logic ---
|
||||
void getFibbTime(myTime t, int *res) {
|
||||
int minute = t.minute;
|
||||
minute = (minute / 5) * 5;
|
||||
int hour = t.hour % 12;
|
||||
if(hour == 0){
|
||||
if (hour == 0) {
|
||||
hour = 12;
|
||||
}
|
||||
int fibBlocks[5] = {5,3,2,1,1};
|
||||
int mins[5] = {0,0,0,0,0};
|
||||
int hours[5] = {0,0,0,0,0};
|
||||
int fibBlocks[5] = {5, 3, 2, 1, 1};
|
||||
int mins[5] = {0, 0, 0, 0, 0};
|
||||
int hours[5] = {0, 0, 0, 0, 0};
|
||||
int blockElement = 0;
|
||||
while(minute != 0){
|
||||
if(fibBlocks[blockElement] * 5 > minute){
|
||||
|
||||
// Calculate minutes representation
|
||||
while (minute != 0) {
|
||||
if (blockElement >= 5) break; // Safety break
|
||||
if (fibBlocks[blockElement] * 5 > minute) {
|
||||
blockElement++;
|
||||
continue;
|
||||
}
|
||||
mins[blockElement] = 1;
|
||||
minute -= fibBlocks[blockElement] * 5;
|
||||
minute -= fibBlocks[blockElement] * 5;
|
||||
blockElement++;
|
||||
|
||||
}
|
||||
|
||||
// Calculate hours representation
|
||||
blockElement = 0;
|
||||
while(hour != 0){
|
||||
if(fibBlocks[blockElement] > hour){
|
||||
while (hour != 0) {
|
||||
if (blockElement >= 5) break; // Safety break
|
||||
if (fibBlocks[blockElement] > hour) {
|
||||
blockElement++;
|
||||
continue;
|
||||
}
|
||||
hours[blockElement] = 1;
|
||||
hour -= fibBlocks[blockElement];
|
||||
hour -= fibBlocks[blockElement];
|
||||
blockElement++;
|
||||
}
|
||||
|
||||
for(int i=0; i < 5; ++i){
|
||||
// Combine results (0=off, 1=min, 2=hour, 3=both)
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
*(res + i) = mins[i] + 2 * hours[i];
|
||||
}
|
||||
|
||||
if((*(res + 3) == 3) && (*(res + 4) ==0)){
|
||||
*(res + 3) = 2;
|
||||
*(res + 4) = 1;
|
||||
// Handle ambiguity of the two '1' blocks
|
||||
if ((*(res + 3) == 3) && (*(res + 4) == 0)) {
|
||||
*(res + 3) = 2; // Assign first '1' block to hour
|
||||
*(res + 4) = 1; // Assign second '1' block to minute
|
||||
}
|
||||
}
|
||||
|
||||
// --- Drawing Functions ---
|
||||
void drawRectangle(cairo_t *cr, rectangle R) {
|
||||
if (R.width <= 0 || R.height <= 0) return; // Don't draw invalid rectangles
|
||||
color rectColor = colors[R.color]; // colors is defined in config.h
|
||||
cairo_rectangle(cr, R.pos_x, R.pos_y, R.width, R.height); /* set rectangle */
|
||||
cairo_set_source_rgb(cr, rectColor.red, rectColor.green, rectColor.blue); /* set fill color */
|
||||
cairo_fill(cr); /* fill rectangle */
|
||||
}
|
||||
|
||||
// Modified to draw the clock centered within a specific target area
|
||||
void drawFibbTimeOnArea(cairo_t *cr, int target_x, int target_y, int target_width, int target_height) {
|
||||
|
||||
// Calculate available drawing area within the target rectangle, considering offsets from config.h
|
||||
int available_width = target_width - LEFT_OFFSET - RIGHT_OFFSET;
|
||||
int available_height = target_height - TOP_OFFSET - BOTTOM_OFFSET;
|
||||
|
||||
if (available_width <= 0 || available_height <= 0) return; // Area too small
|
||||
|
||||
// Calculate unit size based on available height and desired ratio
|
||||
int bh = ((int)(available_height * BOX_HEIGHT_RATE) / 5) * 5;
|
||||
int unit = bh / 5;
|
||||
if (unit <= 0) return; // Unit size too small
|
||||
|
||||
// Calculate total size of the Fibonacci layout (8 units wide, 5 units high)
|
||||
int total_layout_width = 8 * unit;
|
||||
int total_layout_height = 5 * unit;
|
||||
|
||||
// Check if layout fits within available space
|
||||
if (total_layout_width > available_width || total_layout_height > available_height) {
|
||||
// Optionally scale down or just don't draw if it doesn't fit?
|
||||
// For simplicity, let's just prevent drawing very small layouts if unit is tiny above.
|
||||
// A more robust solution might scale 'unit' based on width too.
|
||||
}
|
||||
|
||||
}
|
||||
// Calculate starting position to center the layout within the available area
|
||||
int startpos_x = target_x + LEFT_OFFSET + (available_width - total_layout_width) / 2;
|
||||
int startpos_y = target_y + TOP_OFFSET + (available_height - total_layout_height) / 2;
|
||||
|
||||
void drawRectangle(cairo_t* cr, rectangle R){
|
||||
color rectColor = colors[R.color]; // colors is defined in config.h
|
||||
cairo_rectangle(cr, R.pos_x, R.pos_y, R.width, R.height); /* set rectangle */
|
||||
cairo_set_source_rgb(cr, rectColor.red, rectColor.green, rectColor.blue); /* set fill color */
|
||||
cairo_fill(cr); /* fill rectangle */
|
||||
}
|
||||
|
||||
|
||||
|
||||
void drawFibbTime(cairo_t* cr, int width, int height){
|
||||
|
||||
cairo_set_source_rgb(cr, 0, 0, 0); // setting black background color
|
||||
cairo_paint(cr); // Paint the surface with the source color
|
||||
|
||||
int bh = ((int) ((height - TOP_OFFSET) * BOX_HEIGHT_RATE) / 5) * 5;
|
||||
int unit = bh / 5;
|
||||
|
||||
int startpos_y = STARTPOS_Y + TOP_OFFSET + ((height - TOP_OFFSET - bh)/2);
|
||||
int startpos_x = (width - 8 * unit) / 2;
|
||||
|
||||
myTime t=getTime();
|
||||
int colors[5];
|
||||
getFibbTime(t, (int *) &colors);
|
||||
// Get time and calculate colors
|
||||
myTime t = getTime();
|
||||
int block_colors[5]; // Renamed from 'colors' to avoid conflict with config.h 'colors' array
|
||||
getFibbTime(t, (int *)&block_colors);
|
||||
rectangle Rs[5];
|
||||
|
||||
// TOP-LEFT BOX ( size 2 )
|
||||
// Define rectangle geometry relative to startpos_x/y and unit
|
||||
// TOP-LEFT BOX ( size 2 ) - Index 2
|
||||
Rs[2].pos_x = startpos_x;
|
||||
Rs[2].pos_y = startpos_y;
|
||||
Rs[2].width = 2 * unit - GAP_SIZE;
|
||||
Rs[2].height = Rs[2].width;
|
||||
Rs[2].color = colors[2];
|
||||
Rs[2].color = block_colors[2];
|
||||
|
||||
// BOTTOM-LEFT BOX ( size 3 )
|
||||
// BOTTOM-LEFT BOX ( size 3 ) - Index 1
|
||||
Rs[1].pos_x = Rs[2].pos_x;
|
||||
Rs[1].pos_y = Rs[2].pos_y + (2 * unit);
|
||||
Rs[1].width = 3 * unit - GAP_SIZE;
|
||||
Rs[1].height = Rs[1].width;
|
||||
Rs[1].color = colors[1];
|
||||
Rs[1].color = block_colors[1];
|
||||
|
||||
// TOP SMALL BOX ( size 1 )
|
||||
// TOP SMALL BOX ( size 1 ) - Index 3
|
||||
Rs[3].pos_x = Rs[2].pos_x + (2 * unit);
|
||||
Rs[3].pos_y = Rs[2].pos_y;
|
||||
Rs[3].width = 1 * unit - GAP_SIZE;
|
||||
Rs[3].height = Rs[3].width;
|
||||
Rs[3].color = colors[3];
|
||||
Rs[3].color = block_colors[3];
|
||||
|
||||
// BOTTOM SMALL BOX ( size 1 )
|
||||
// BOTTOM SMALL BOX ( size 1 ) - Index 4
|
||||
Rs[4].pos_x = Rs[3].pos_x;
|
||||
Rs[4].pos_y = Rs[3].pos_y + (1 * unit);
|
||||
Rs[4].width = Rs[3].width;
|
||||
Rs[4].height = Rs[3].width;
|
||||
Rs[4].color = colors[4];
|
||||
Rs[4].color = block_colors[4];
|
||||
|
||||
// LARGE BOX ( size 5 )
|
||||
// LARGE BOX ( size 5 ) - Index 0
|
||||
Rs[0].pos_x = Rs[3].pos_x + (1 * unit);
|
||||
Rs[0].pos_y = Rs[2].pos_y;
|
||||
Rs[0].width = 5 * unit - GAP_SIZE;
|
||||
Rs[0].height = Rs[0].width;
|
||||
Rs[0].color = colors[0];
|
||||
Rs[0].color = block_colors[0];
|
||||
|
||||
// Draw the rectangles
|
||||
// Order might matter visually if gaps are negative, draw largest last? Or smallest?
|
||||
// Current order seems fine for positive gaps.
|
||||
drawRectangle(cr, Rs[1]);
|
||||
drawRectangle(cr, Rs[2]);
|
||||
drawRectangle(cr, Rs[3]);
|
||||
@ -163,58 +193,105 @@ void drawFibbTime(cairo_t* cr, int width, int height){
|
||||
drawRectangle(cr, Rs[0]);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// --- Main Function ---
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if(argc > 1){
|
||||
if(strcmp(argv[1],"-v") == 0){
|
||||
printf("%s\n", VERSION);
|
||||
// --- Argument Parsing ---
|
||||
if (argc > 1) {
|
||||
if (strcmp(argv[1], "-v") == 0) {
|
||||
printf("fiboBG Version: %s\n", VERSION);
|
||||
exit(0);
|
||||
}
|
||||
// Add other arguments here if needed
|
||||
}
|
||||
|
||||
int width;
|
||||
int height;
|
||||
// --- X11 Setup ---
|
||||
Display *d = XOpenDisplay(NULL);
|
||||
assert(d);
|
||||
if (!d) {
|
||||
fprintf(stderr, "Error: Cannot open display.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int s = DefaultScreen(d);
|
||||
Window w = RootWindow(d, s);
|
||||
XSync(d, False);
|
||||
width = DisplayWidth(d, s);
|
||||
height = DisplayHeight(d, s);
|
||||
Pixmap pix;
|
||||
int depth = DefaultDepth(d, s);
|
||||
pix = XCreatePixmap(d, w, width, height, depth);
|
||||
Atom prop_root;
|
||||
int screen_num = DefaultScreen(d);
|
||||
Window root_window = RootWindow(d, screen_num);
|
||||
int root_width = DisplayWidth(d, screen_num); // Full virtual screen width
|
||||
int root_height = DisplayHeight(d, screen_num); // Full virtual screen height
|
||||
int root_depth = DefaultDepth(d, screen_num);
|
||||
|
||||
prop_root = XInternAtom(d, "_XROOTPMAP_ID", False);
|
||||
Pixmap pix = XCreatePixmap(d, root_window, root_width, root_height, root_depth);
|
||||
Atom prop_root_pixmap = XInternAtom(d, "_XROOTPMAP_ID", False);
|
||||
|
||||
// --- Cairo Setup ---
|
||||
cairo_surface_t *surf = cairo_xlib_surface_create(d, pix,
|
||||
DefaultVisual(d, s),
|
||||
width, height);
|
||||
DefaultVisual(d, screen_num),
|
||||
root_width, root_height);
|
||||
cairo_t *cr = cairo_create(surf);
|
||||
|
||||
// --- Register Signal Handlers ---
|
||||
signal(SIGINT, handle_signal); // <--- Catch Ctrl+C
|
||||
signal(SIGTERM, handle_signal); // <--- Catch standard termination signal
|
||||
|
||||
// --- Main Loop ---
|
||||
while(running){ // <--- Changed loop condition
|
||||
drawFibbTime(cr, width, height);
|
||||
XChangeProperty(d, w, prop_root, XA_PIXMAP, 32, PropModeReplace,
|
||||
(unsigned char*) &pix, 1);
|
||||
// --- Xinerama Setup ---
|
||||
XineramaScreenInfo *xinerama_screens = NULL;
|
||||
int num_screens = 0;
|
||||
int xinerama_major, xinerama_minor;
|
||||
Bool xinerama_available = XineramaQueryExtension(d, &xinerama_major, &xinerama_minor);
|
||||
|
||||
XSetWindowBackgroundPixmap(d, w, pix); // If we do not have compositor :)
|
||||
XClearWindow(d,w);
|
||||
XFlush(d);
|
||||
sleep(REFRESH_TIME);
|
||||
if (xinerama_available && XineramaIsActive(d)) {
|
||||
xinerama_screens = XineramaQueryScreens(d, &num_screens);
|
||||
if (!xinerama_screens || num_screens <= 0) {
|
||||
fprintf(stderr, "Warning: Xinerama active but failed to query screens or none found. Falling back to single screen mode.\n");
|
||||
num_screens = 0; // Force fallback
|
||||
if (xinerama_screens) XFree(xinerama_screens); // Free if allocated but num_screens was 0
|
||||
xinerama_screens = NULL;
|
||||
} else {
|
||||
printf("Info: Xinerama detected %d screen(s).\n", num_screens);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Info: Xinerama not available or not active. Running in single screen mode.\n");
|
||||
num_screens = 0; // Ensure fallback
|
||||
}
|
||||
|
||||
// --- Cleanup (Now reachable) ---
|
||||
printf("\nFibonacci Clock: Shutting down gracefully...\n"); // Optional message
|
||||
|
||||
// --- Register Signal Handlers ---
|
||||
signal(SIGINT, handle_signal); // Catch Ctrl+C
|
||||
signal(SIGTERM, handle_signal); // Catch standard termination signal
|
||||
|
||||
// --- Main Loop ---
|
||||
printf("Fibonacci Clock: Starting background update loop...\n");
|
||||
while (running) {
|
||||
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
|
||||
cairo_paint(cr);
|
||||
|
||||
if (num_screens > 0 && xinerama_screens) {
|
||||
for (int i = 0; i < num_screens; ++i) {
|
||||
drawFibbTimeOnArea(cr,
|
||||
xinerama_screens[i].x_org,
|
||||
xinerama_screens[i].y_org,
|
||||
xinerama_screens[i].width,
|
||||
xinerama_screens[i].height);
|
||||
}
|
||||
} else {
|
||||
drawFibbTimeOnArea(cr, 0, 0, root_width, root_height);
|
||||
}
|
||||
|
||||
XChangeProperty(d, root_window, prop_root_pixmap, XA_PIXMAP, 32, PropModeReplace,
|
||||
(unsigned char *)&pix, 1);
|
||||
|
||||
XSetWindowBackgroundPixmap(d, root_window, pix);
|
||||
XClearWindow(d, root_window);
|
||||
|
||||
XFlush(d);
|
||||
|
||||
sleep(REFRESH_TIME);
|
||||
}
|
||||
|
||||
// --- Cleanup ---
|
||||
printf("\nFibonacci Clock: Shutting down gracefully...\n");
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_destroy(surf);
|
||||
XFreePixmap(d, pix);
|
||||
if (xinerama_screens) { // Free Xinerama screen info if allocated
|
||||
XFree(xinerama_screens);
|
||||
}
|
||||
XCloseDisplay(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user