mirror of https://github.com/dperelman/gluqlo
508 lines
13 KiB
C
508 lines
13 KiB
C
/*
|
|
* Gluqlo: Fliqlo for Linux
|
|
* https://github.com/alexanderk23/gluqlo
|
|
*
|
|
* Copyright (c) 2010-2012 Kuźniarski Jacek
|
|
* Copyright (c) 2014 Alexander Kovalenko
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <X11/Xlib.h>
|
|
#include <time.h>
|
|
|
|
#include "SDL.h"
|
|
#include "SDL_ttf.h"
|
|
#include "SDL_syswm.h"
|
|
#include "SDL_gfxPrimitives.h"
|
|
#include "SDL_rotozoom.h"
|
|
|
|
#ifndef FONT
|
|
#define FONT "/usr/share/gluqlo/gluqlo.ttf"
|
|
#endif
|
|
|
|
const char* TITLE = "Gluqlo 1.1";
|
|
const int DEFAULT_WIDTH = 1024;
|
|
const int DEFAULT_HEIGHT = 768;
|
|
|
|
bool twentyfourh = true;
|
|
bool leadingzero = false;
|
|
bool fullscreen = false;
|
|
bool animate = true;
|
|
|
|
int past_h = -1, past_m = -1;
|
|
|
|
int width = DEFAULT_WIDTH;
|
|
int height = DEFAULT_HEIGHT;
|
|
|
|
TTF_Font *font_time = NULL;
|
|
TTF_Font *font_mode = NULL;
|
|
|
|
const SDL_Color FONT_COLOR = { 0xb7, 0xb7, 0xb7 };
|
|
const SDL_Color BACKGROUND_COLOR = { 0x0f, 0x0f, 0x0f };
|
|
|
|
SDL_Surface *screen;
|
|
|
|
SDL_Rect hourBackground;
|
|
SDL_Rect minBackground;
|
|
|
|
SDL_Rect bgrect;
|
|
SDL_Surface *bg;
|
|
|
|
// draw rounded box
|
|
// see http://lists.libsdl.org/pipermail/sdl-libsdl.org/2006-December/058868.html
|
|
void fill_rounded_box_b(SDL_Surface* dst, SDL_Rect *coords, int r, SDL_Color color) {
|
|
Uint32 pixcolor = SDL_MapRGB(dst->format, color.r, color.g, color.b);
|
|
|
|
int i, j;
|
|
int rpsqrt2 = (int) (r / sqrt(2));
|
|
int yd = dst->pitch / dst->format->BytesPerPixel;
|
|
int w = coords->w / 2 - 1;
|
|
int h = coords->h / 2 - 1;
|
|
int xo = coords->x + w;
|
|
int yo = coords->y + h;
|
|
|
|
w -= r;
|
|
h -= r;
|
|
|
|
if(w <= 0 || h <= 0) return;
|
|
|
|
SDL_LockSurface(dst);
|
|
Uint32 *pixels = (Uint32*)(dst->pixels);
|
|
|
|
int sy = (yo - h) * yd;
|
|
int ey = (yo + h) * yd;
|
|
int sx = xo - w;
|
|
int ex = xo + w;
|
|
|
|
for(i = sy; i <= ey; i += yd)
|
|
for(j = sx - r; j <= ex + r; j++)
|
|
pixels[i + j] = pixcolor;
|
|
|
|
int d = -r;
|
|
int x2m1 = -1;
|
|
int y = r;
|
|
|
|
for(int x = 0; x <= rpsqrt2; x++) {
|
|
x2m1 += 2;
|
|
d += x2m1;
|
|
if(d >= 0) {
|
|
y--;
|
|
d -= y * 2;
|
|
}
|
|
|
|
for(i = sx - x; i <= ex + x; i++) {
|
|
pixels[sy - y * yd + i] = pixcolor;
|
|
}
|
|
|
|
for(i = sx - y; i <= ex + y; i++) {
|
|
pixels[sy - x * yd + i] = pixcolor;
|
|
}
|
|
|
|
for(i = sx - y; i <= ex + y; i++) {
|
|
pixels[ey + x * yd + i] = pixcolor;
|
|
}
|
|
|
|
for(i = sx - x; i <= ex + x; i++) {
|
|
pixels[ey + y * yd + i] = pixcolor;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(dst);
|
|
}
|
|
|
|
void render_ampm(SDL_Surface *surface, SDL_Rect *rect, int pm) {
|
|
char mode[3];
|
|
SDL_Rect coords;
|
|
snprintf(mode, 3, "%cM", pm ? 'P' : 'A');
|
|
SDL_Surface *ampm = TTF_RenderText_Blended(font_mode, mode, FONT_COLOR);
|
|
int offset = rect->h * 0.127;
|
|
coords.x = rect->x + rect->h * 0.07;
|
|
coords.y = rect->y + (pm ? rect->h - offset - ampm->h : offset);
|
|
SDL_BlitSurface(ampm, 0, surface, &coords);
|
|
SDL_FreeSurface(ampm);
|
|
}
|
|
|
|
|
|
|
|
void blit_digits(SDL_Surface *surface, SDL_Rect *rect, int spc, char digits[], SDL_Color color) {
|
|
int min_x, max_x, min_y, max_y, advance;
|
|
int adjust_x = (digits[0] == '1') ? 2.5 * spc : 0; // special case
|
|
int center_x = rect->x + rect->w / 2 - adjust_x;
|
|
|
|
SDL_Surface *glyph;
|
|
SDL_Rect coords;
|
|
|
|
if(digits[1]) {
|
|
// first digit
|
|
TTF_GlyphMetrics(font_time, digits[0], &min_x, &max_x, &min_y, &max_y, &advance);
|
|
glyph = TTF_RenderGlyph_Blended(font_time, digits[0], color);
|
|
coords.x = center_x - max_x + min_x - spc - (adjust_x ? spc : 0);
|
|
coords.y = rect->y + (rect->h - glyph->h) / 2;
|
|
SDL_BlitSurface(glyph, 0, surface, &coords);
|
|
SDL_FreeSurface(glyph);
|
|
// second digit
|
|
TTF_GlyphMetrics(font_time, digits[1], &min_x, &max_x, &min_y, &max_y, &advance);
|
|
glyph = TTF_RenderGlyph_Blended(font_time, digits[1], color);
|
|
coords.y = rect->y + (rect->h - glyph->h) / 2;
|
|
coords.x = center_x + spc / 2;
|
|
SDL_BlitSurface(glyph, 0, surface, &coords);
|
|
SDL_FreeSurface(glyph);
|
|
} else {
|
|
// single digit
|
|
glyph = TTF_RenderGlyph_Blended(font_time, digits[0], color);
|
|
coords.x = center_x - glyph->w / 2;
|
|
coords.y = rect->y + (rect->h - glyph->h) / 2;
|
|
SDL_BlitSurface(glyph, 0, surface, &coords);
|
|
SDL_FreeSurface(glyph);
|
|
}
|
|
}
|
|
|
|
|
|
void render_digits(SDL_Surface *surface, SDL_Rect *background, char digits[], char prevdigits[], int maxsteps, int step) {
|
|
SDL_Rect rect, dstrect;
|
|
SDL_Color color;
|
|
double scale;
|
|
Uint8 c;
|
|
|
|
int spc = surface->h * .0125;
|
|
|
|
// blit upper halves of current digits
|
|
rect.x = background->x;
|
|
rect.y = background->y;
|
|
rect.w = background->w;
|
|
rect.h = background->h/2;
|
|
SDL_SetClipRect(surface, &rect);
|
|
SDL_BlitSurface(bg, 0, surface, &rect);
|
|
blit_digits(surface, background, spc, digits, FONT_COLOR);
|
|
SDL_SetClipRect(surface, NULL);
|
|
|
|
int halfsteps = maxsteps / 2;
|
|
int upperhalf = (step+1) <= halfsteps;
|
|
if(upperhalf) {
|
|
scale = 1.0 - (1.0 * step) / (halfsteps - 1);
|
|
c = 0xb7 - 0xb7 * (1.0 * step) / (halfsteps - 1);
|
|
} else {
|
|
scale = ((1.0 * step) - halfsteps + 1) / halfsteps;
|
|
c = 0xb7 * ((1.0 * step) - halfsteps + 1) / halfsteps;
|
|
}
|
|
color.r = color.g = color.b = c;
|
|
|
|
// create surface to scale from filled background surface
|
|
SDL_Surface *bgcopy = SDL_ConvertSurface(bg, bg->format, bg->flags);
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.w = bgcopy->w;
|
|
rect.h = bgcopy->h;
|
|
blit_digits(bgcopy, &rect, spc, upperhalf ? prevdigits : digits, color);
|
|
|
|
// scale and blend it to dest
|
|
SDL_Surface *scaled = zoomSurface(bgcopy, 1.0, scale, 1);
|
|
rect.x = 0;
|
|
rect.y = upperhalf ? 0 : scaled->h / 2;
|
|
rect.w = scaled->w;
|
|
rect.h = scaled->h / 2;
|
|
dstrect.x = background->x;
|
|
dstrect.y = background->y + ( upperhalf ? ((background->h - scaled->h) / 2) : background->h / 2);
|
|
dstrect.w = rect.w;
|
|
dstrect.h = rect.h;
|
|
SDL_SetClipRect(surface, &dstrect);
|
|
SDL_BlitSurface(scaled, &rect, surface, &dstrect);
|
|
SDL_SetClipRect(surface, NULL);
|
|
SDL_FreeSurface(scaled);
|
|
SDL_FreeSurface(bgcopy);
|
|
|
|
if(!animate) return;
|
|
// draw divider
|
|
rect.h = surface->h * 0.005;
|
|
rect.w = background->w;
|
|
rect.x = background->x;
|
|
rect.y = background->y + (background->h - rect.h) / 2;
|
|
SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, 0, 0, 0));
|
|
rect.y += rect.h;
|
|
rect.h = 1;
|
|
SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, 0x1a, 0x1a, 0x1a));
|
|
}
|
|
|
|
void render_clock(int maxsteps, int step) {
|
|
char buffer[3], buffer2[3];
|
|
struct tm *_time;
|
|
time_t rawtime;
|
|
|
|
time(&rawtime);
|
|
_time = localtime(&rawtime);
|
|
|
|
// draw hours
|
|
if(_time->tm_hour != past_h) {
|
|
int h = twentyfourh ? _time->tm_hour : (_time->tm_hour + 11) % 12 + 1;
|
|
if(leadingzero) {
|
|
snprintf(buffer, 3, "%02d", h);
|
|
snprintf(buffer2, 3, "%02d", past_h);
|
|
} else {
|
|
snprintf(buffer, 3, "%d", h);
|
|
snprintf(buffer2, 3, "%d", past_h);
|
|
}
|
|
render_digits(screen, &hourBackground, buffer, buffer2, maxsteps, step);
|
|
// draw am/pm
|
|
if(!twentyfourh) render_ampm(screen, &hourBackground, _time->tm_hour >= 12);
|
|
}
|
|
|
|
// draw minutes
|
|
if(_time->tm_min != past_m) {
|
|
snprintf(buffer, 3, "%02d", _time->tm_min);
|
|
snprintf(buffer2, 3, "%02d", past_m);
|
|
render_digits(screen, &minBackground, buffer, buffer2, maxsteps, step);
|
|
}
|
|
|
|
// flip backbuffer
|
|
SDL_Flip(screen);
|
|
|
|
if(step == maxsteps-1) {
|
|
past_h = _time->tm_hour;
|
|
past_m = _time->tm_min;
|
|
}
|
|
}
|
|
|
|
void render_animation() {
|
|
if(!animate) {
|
|
render_clock(20, 19);
|
|
return;
|
|
}
|
|
|
|
const int DURATION = 260;
|
|
int start_tick = SDL_GetTicks();
|
|
int end_tick = start_tick + DURATION;
|
|
int current_tick;
|
|
int frame;
|
|
int done = 0;
|
|
|
|
while(!done) {
|
|
current_tick = SDL_GetTicks();
|
|
if(current_tick >= end_tick) {
|
|
done = 1;
|
|
current_tick = end_tick;
|
|
}
|
|
frame = 99 * (current_tick-start_tick) / (end_tick-start_tick);
|
|
render_clock(100, frame);
|
|
}
|
|
}
|
|
|
|
Uint32 update_time(Uint32 interval, void *param) {
|
|
SDL_Event e;
|
|
time_t rawtime;
|
|
struct tm *time_i;
|
|
|
|
time(&rawtime);
|
|
time_i = localtime(&rawtime);
|
|
|
|
if(time_i->tm_min != past_m) {
|
|
e.type = SDL_USEREVENT;
|
|
e.user.code = 0;
|
|
e.user.data1 = NULL;
|
|
e.user.data2 = NULL;
|
|
SDL_PushEvent(&e);
|
|
interval = 1000 * (60 - time_i->tm_sec) - 250;
|
|
} else {
|
|
interval = 250;
|
|
}
|
|
|
|
return interval;
|
|
}
|
|
|
|
int main(int argc, char** argv ) {
|
|
char *wid_env;
|
|
static char sdlwid[100];
|
|
double display_scale_factor = 1;
|
|
|
|
Uint32 wid = 0;
|
|
Display *display;
|
|
XWindowAttributes windowAttributes;
|
|
|
|
for(int i = 1; i < argc; i++) {
|
|
if(strcmp("--help",argv[i]) == 0 || strcmp("-help", argv[i]) == 0) {
|
|
printf("Usage: %s [OPTION...]\nOptions:\n", argv[0]);
|
|
printf(" -help\t\tDisplay this\n");
|
|
printf(" -root, -f\tFullscreen\n");
|
|
printf(" -noflip\t\tDisable the flip animation (change time in one frame)\n");
|
|
printf(" -ampm\t\tUse 12-hour clock format (AM/PM)\n");
|
|
printf(" -leadingzero\t\tAlways display hour with two digits\n");
|
|
printf(" -w\t\tCustom width\n");
|
|
printf(" -h\t\tCustom height\n");
|
|
printf(" -r\t\tCustom resolution in WxH format\n");
|
|
printf(" -s\t\tCustom display scale factor\n");
|
|
return 0;
|
|
} else if(strcmp("-root", argv[i]) == 0 || strcmp("-f", argv[i]) == 0 || strcmp("--fullscreen", argv[i]) == 0) {
|
|
fullscreen = true;
|
|
} else if(strcmp("-noflip", argv[i]) == 0) {
|
|
animate = false;
|
|
} else if(strcmp("-ampm", argv[i]) == 0) {
|
|
twentyfourh = false;
|
|
} else if(strcmp("-leadingzero", argv[i]) == 0) {
|
|
leadingzero = true;
|
|
} else if(strcmp("-r", argv[i]) == 0 || strcmp("--resolution", argv[i]) == 0) {
|
|
char *resolution = argv[i+1];
|
|
char *val = strtok(resolution, "x");
|
|
width = atoi(val);
|
|
val = strtok(NULL, "x");
|
|
height = atoi(val);
|
|
i++;
|
|
} else if(strcmp("-w", argv[i]) == 0) {
|
|
width = atoi(argv[i+1]);
|
|
i++;
|
|
} else if(strcmp("-h", argv[i]) == 0) {
|
|
height = atoi(argv[i+1]);
|
|
i++;
|
|
} else if(strcmp("-s", argv[i]) == 0) {
|
|
display_scale_factor = atof(argv[i+1]);
|
|
i++;
|
|
} else if(strcmp("-window-id", argv[i]) == 0) {
|
|
wid = strtol(argv[i+1], (char **) NULL, 0);
|
|
i++;
|
|
} else {
|
|
printf("Invalid option -- %s\n", argv[i]);
|
|
printf("Try --help for more information.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* If no window argument, check environment */
|
|
if(wid == 0) {
|
|
if ((wid_env = getenv("XSCREENSAVER_WINDOW")) != NULL ) {
|
|
wid = strtol(wid_env, (char **) NULL, 0); /* Base 0 autodetects hex/dec */
|
|
}
|
|
}
|
|
|
|
/* Get win attrs if we've been given a window, otherwise we'll use our own */
|
|
if(wid != 0) {
|
|
if ((display = XOpenDisplay(NULL)) != NULL) { /* Use the default display */
|
|
XGetWindowAttributes(display, (Window) wid, &windowAttributes);
|
|
XCloseDisplay(display);
|
|
snprintf(sdlwid, 100, "SDL_WINDOWID=0x%X", wid);
|
|
putenv(sdlwid); /* Tell SDL to use this window */
|
|
width = windowAttributes.width;
|
|
height = windowAttributes.height;
|
|
}
|
|
}
|
|
|
|
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
|
|
fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
|
|
return 1;
|
|
}
|
|
atexit(SDL_Quit);
|
|
|
|
if(fullscreen && (!wid)) {
|
|
screen = SDL_SetVideoMode(0, 0, 32, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_FULLSCREEN);
|
|
} else {
|
|
screen = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE|SDL_DOUBLEBUF);
|
|
}
|
|
|
|
if (!screen) {
|
|
fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
if(fullscreen || wid) {
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
}
|
|
|
|
SDL_WM_SetCaption(TITLE, TITLE);
|
|
|
|
width = screen->w * display_scale_factor;
|
|
height = screen->h * display_scale_factor;
|
|
|
|
TTF_Init();
|
|
atexit(TTF_Quit);
|
|
font_time = TTF_OpenFont(FONT, height / 1.68 );
|
|
font_mode = TTF_OpenFont(FONT, height / 16.5);
|
|
if (!font_time || !font_mode) {
|
|
fprintf(stderr, "TTF_OpenFont: %s\n", TTF_GetError());
|
|
return 1;
|
|
}
|
|
|
|
// clear screen
|
|
SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
|
|
|
|
// calculate box coordinates
|
|
int rectsize = height * 0.6;
|
|
int spacing = width * .031;
|
|
int radius = height * .05714;
|
|
|
|
int jitter_width = 1;
|
|
int jitter_height = 1;
|
|
if (display_scale_factor != 1) {
|
|
jitter_width = (screen->w - width) * 0.5;
|
|
jitter_height = (screen->h - height) * 0.5;
|
|
}
|
|
|
|
|
|
hourBackground.x = 0.5 * (width - (0.031 * width) - (1.2 * height))
|
|
+ jitter_width;
|
|
hourBackground.y = 0.2 * height + jitter_height;
|
|
hourBackground.w = rectsize ;
|
|
hourBackground.h = rectsize ;
|
|
|
|
minBackground.x = hourBackground.x + (0.6 * height) + spacing;
|
|
minBackground.y = hourBackground.y;
|
|
minBackground.w = rectsize;
|
|
minBackground.h = rectsize;
|
|
|
|
// create background surface
|
|
bgrect.x = 0;
|
|
bgrect.y = 0;
|
|
bgrect.w = rectsize;
|
|
bgrect.h = rectsize;
|
|
bg = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA, rectsize, rectsize, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
|
|
fill_rounded_box_b(bg, &bgrect, radius, BACKGROUND_COLOR);
|
|
|
|
// draw current time
|
|
render_clock(20, 19);
|
|
|
|
// main loop
|
|
bool done = false;
|
|
SDL_Event event;
|
|
SDL_TimerID timer = SDL_AddTimer(60, update_time, NULL);
|
|
|
|
while(!done && SDL_WaitEvent(&event)) {
|
|
switch(event.type) {
|
|
case SDL_USEREVENT:
|
|
render_animation();
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
switch(event.key.keysym.sym) {
|
|
case SDLK_ESCAPE:
|
|
case SDLK_q:
|
|
done = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SDL_QUIT:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_RemoveTimer(timer);
|
|
|
|
SDL_FreeSurface(bg);
|
|
SDL_FreeSurface(screen);
|
|
|
|
TTF_CloseFont(font_time);
|
|
TTF_CloseFont(font_mode);
|
|
|
|
return 0;
|
|
}
|