PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/xminuto.c

https://bitbucket.org/x31eq/binaryclock
C | 232 lines | 199 code | 28 blank | 5 comment | 41 complexity | 6a9eb0d4f7d871b7a785686b4b0fc6d4 MD5 | raw file
  1. #include <X11/Xlib.h>
  2. #include <X11/Xatom.h>
  3. #include <X11/Xutil.h>
  4. #include <sys/select.h>
  5. #include <string.h>
  6. #include <time.h>
  7. #include <stdlib.h>
  8. #include <unistd.h>
  9. #ifdef DOMINO
  10. const int cols = 3;
  11. #else
  12. const int cols = 4;
  13. #endif
  14. struct options {
  15. int minimal;
  16. int background;
  17. int x;
  18. int y;
  19. int width;
  20. };
  21. void get_current_time(struct tm *local_time, struct timeval *wait_time) {
  22. struct timespec timestamp;
  23. int fails = 0;
  24. while (clock_gettime(CLOCK_REALTIME, &timestamp)) {
  25. if (fails++ > 10) exit(3);
  26. }
  27. long nsec_offset = timestamp.tv_nsec;
  28. time_t current_second = timestamp.tv_sec;
  29. localtime_r(&current_second, local_time);
  30. wait_time->tv_usec = 1000000 - nsec_offset / 1000;
  31. wait_time->tv_sec = (local_time->tm_sec > 58)? 0: 59 - local_time->tm_sec;
  32. }
  33. void draw_clock(const struct tm *lt, Display *display, Window window) {
  34. XWindowAttributes attributes;
  35. int screen = DefaultScreen(display);
  36. unsigned long black = BlackPixel(display, screen);
  37. unsigned long white = WhitePixel(display, screen);
  38. GC gc = XCreateGC(display, window, 0, 0);
  39. XGetWindowAttributes(display, window, &attributes);
  40. int height = attributes.height - 2;
  41. int width = attributes.width - 2;
  42. int mask = cols == 3? 4: 1;
  43. int x_pos[] = {0, width / cols, width * 2 / cols, width * 3 / cols, width};
  44. int y_pos[] = {0, height / 2, height};
  45. int bits = ((lt->tm_hour << 6) | (lt->tm_min << 4) / 15);
  46. for (int col = cols; col--;) {
  47. for (int row = 2; row--;) {
  48. XSetForeground(display, gc, bits & mask? black: white);
  49. XFillRectangle(display, window, gc,
  50. 1 + x_pos[col], 1 + y_pos[row],
  51. x_pos[col + 1] - x_pos[col],
  52. y_pos[row + 1] - y_pos[row]);
  53. mask <<= 1;
  54. }
  55. }
  56. XSetForeground(display, gc, black);
  57. if (! (bits & (cols == 3? 0xa8: 0xaa))) {
  58. XDrawLine(display, window, gc, (1 + width) / 2, 1,
  59. (1 + width) / 2, height / 4);
  60. }
  61. if (! (bits & (cols == 3? 0x54: 0x55))) {
  62. XDrawLine(display, window, gc, (1 + width) / 2, 1 + 3 * height / 4,
  63. (1 + width) / 2, 1 + height);
  64. }
  65. if (! (bits & 0xc0)) {
  66. XDrawLine(display, window, gc, 1, 1 + height / 2,
  67. width / 8, 1 + height/2);
  68. }
  69. if (! (bits & (cols == 3? 0xc: 3))) {
  70. XDrawLine(display, window, gc, 1 + 7 * width / 8, 1 + height / 2,
  71. 1 + width, 1 + height/2);
  72. }
  73. XSetForeground(display, gc, white);
  74. XDrawLine(display, window, gc, 0, 0, width + 1, 0);
  75. XDrawLine(display, window, gc, 0, 0, 0, height + 1);
  76. XDrawLine(display, window, gc, width + 1, 0, width + 1, height + 1);
  77. XDrawLine(display, window, gc, 0, height + 1, width + 1, height + 1);
  78. XFreeGC(display, gc);
  79. XFlush(display);
  80. };
  81. Window initialize_window(Display *display, struct options *options) {
  82. int width = options->width * cols + 2;
  83. int height = options->width * 2 + 2;
  84. Window window = XCreateSimpleWindow(
  85. display, DefaultRootWindow(display),
  86. 0, 0, width, height, 0, 0, 0);
  87. XSetStandardProperties(display, window, "Minuto", "Minuto",
  88. None, NULL, 0, NULL);
  89. if (options->minimal || options->background) {
  90. // This should make the window undecorated and always on top
  91. Atom windowType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
  92. long typeValue = XInternAtom(display,
  93. options->minimal?
  94. "_NET_WM_WINDOW_TYPE_DOCK": "_NET_WM_WINDOW_TYPE_DESKTOP",
  95. False);
  96. if (windowType != None) {
  97. XChangeProperty(display, window, windowType,
  98. XA_ATOM, 32, PropModeReplace,
  99. (unsigned char *) &typeValue, 1);
  100. }
  101. // Place on all desktops
  102. unsigned long desktop_value = 0xffffffff;
  103. Atom desktop = XInternAtom(display, "_NET_WM_DESKTOP", False);
  104. if (desktop != None) {
  105. XChangeProperty(display, window, desktop,
  106. XA_CARDINAL, 32, PropModeReplace,
  107. (unsigned char *) &desktop_value, 1);
  108. }
  109. // Don't request focus
  110. XWMHints *hints = XAllocWMHints();
  111. hints->flags = InputHint;
  112. hints->input = 0;
  113. XSetWMHints(display, window, hints);
  114. }
  115. // These are defaults for _NET_WM_WINDOW_TYPE_DOCK, at least with Openbox
  116. Atom windowState = XInternAtom(display, "_NET_WM_STATE", False);
  117. long stateValues[2];
  118. stateValues[0] = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
  119. stateValues[1] = XInternAtom(display,
  120. options->minimal? "_NET_WM_STATE_ABOVE": "_NET_WM_STATE_BELOW",
  121. False);
  122. if (windowState != None) {
  123. XChangeProperty(display, window, windowState,
  124. XA_ATOM, 32, PropModeReplace,
  125. (unsigned char *) &stateValues,
  126. options->minimal || options->background? 2: 1);
  127. }
  128. XSelectInput(display, window, ExposureMask | VisibilityChangeMask);
  129. XMapWindow(display, window);
  130. // Looks like this only works after the map
  131. if (options->x || options->y) {
  132. XMoveWindow(display, window, options->x, options->y);
  133. }
  134. XFlush(display);
  135. return window;
  136. }
  137. void set_options(struct options *options, int argc, char *argv[]) {
  138. int opt;
  139. while ((opt = getopt(argc, argv, "mbx:y:w:")) != -1) {
  140. switch(opt) {
  141. case 'm':
  142. options->minimal = 1;
  143. options->x = options->x? options->x: 0x300;
  144. options->width = options->width? options->width: 4;
  145. break;
  146. case 'b':
  147. options->background = 1;
  148. break;
  149. case 'x':
  150. options->x = atoi(optarg);
  151. break;
  152. case 'y':
  153. options->y = atoi(optarg);
  154. break;
  155. case 'w':
  156. options->width = atoi(optarg);
  157. break;
  158. }
  159. }
  160. options->width = options->width? options->width: 0x10;
  161. }
  162. int main(int argc, char *argv[]) {
  163. Display *display;
  164. struct options options = {.minimal = 0, .background = 0,
  165. .x = 0, .y = 0, .width = 0};
  166. XEvent event;
  167. struct tm local_time;
  168. struct timeval wait_time = {.tv_sec = 1, .tv_usec = 0};
  169. fd_set in_fds;
  170. set_options(&options, argc, argv);
  171. display = XOpenDisplay(NULL);
  172. if (display == NULL) return 1;
  173. Window window = initialize_window(display, &options);
  174. int file_descriptor = ConnectionNumber(display);
  175. Atom deleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", 0);
  176. if (deleteWindow != None) {
  177. XSetWMProtocols(display, window, &deleteWindow, 1);
  178. }
  179. int visible = 1;
  180. while (1) {
  181. FD_ZERO(&in_fds);
  182. FD_SET(file_descriptor, &in_fds);
  183. int num_fds = select(file_descriptor + 1, &in_fds, NULL, NULL,
  184. visible? &wait_time: NULL);
  185. if (num_fds < 0) return 2;
  186. while (XPending(display)) {
  187. XNextEvent(display, &event);
  188. if (event.type == ClientMessage
  189. && event.xclient.data.l[0] == deleteWindow) {
  190. XDestroyWindow(display, window);
  191. XCloseDisplay(display);
  192. return 0;
  193. }
  194. else if (event.type == VisibilityNotify) {
  195. visible = event.xvisibility.state
  196. != VisibilityFullyObscured;
  197. }
  198. }
  199. if (visible) {
  200. get_current_time(&local_time, &wait_time);
  201. draw_clock(&local_time, display, window);
  202. }
  203. }
  204. }