File: breakrem/info.txt
   1 This app keeps reminding you to take breaks from your
   2 computer with pop-ups every %d minutes: time isn't
   3 counted while pop-ups are showing.
   4 
   5 The pop-ups will keep coming regularly as long as you
   6 keep pressing OK to dismiss them when they show up;
   7 pressing Cancel or the ✕ on the top corner will quit
   8 this app and stop the reminders.
   9 
  10 Note: you can start this app from the command-line with
  11 a number between 1 and 20 to set how many minutes to
  12 wait for each break reminder.

     File: breakrem/main.c
   1 /*
   2 Break Reminder
   3 
   4 This app keeps reminding you to take breaks from your computer with pop-ups
   5 every few minutes: time isn't counted while pop-ups are showing.
   6 */
   7 
   8 // gcc -Os -o breakrem.exe -mwindows *.c
   9 
  10 #include <stdio.h>
  11 #include <windows.h>
  12 
  13 const char start_fmt[] = ""
  14 "This app keeps reminding you to take breaks from your\n"
  15 "computer with pop-ups every %d minutes: time isn't\n"
  16 "counted while pop-ups are showing.\n"
  17 "\n"
  18 "The pop-ups will keep coming regularly as long as you\n"
  19 "keep pressing OK to dismiss them when they show up;\n"
  20 "pressing Cancel or the X on the top corner will quit\n"
  21 "this app and stop the reminders.\n"
  22 "\n"
  23 "Note: you can start this app from the command-line with\n"
  24 "a number between 1 and 20 to set how many minutes to\n"
  25 "wait for each break reminder."
  26 "";
  27 
  28 const char break_time_fmt[] = ""
  29 "The %d minutes have passed; time for a break\n"
  30 "\n"
  31 "To start the countdown for the next reminder, press OK\n"
  32 "For no more reminders, press Cancel or X at the top"
  33 "";
  34 
  35 // get_minutes figures out how many minutes to wait between breaks, by parsing
  36 // the 1st cmd-line arg after the app's name, or using a default value
  37 size_t get_minutes() {
  38     int argc;
  39     LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
  40 
  41     const size_t fallback = 15;
  42     if (argc < 2) {
  43         return fallback;
  44     }
  45 
  46     // turn 1st arg from UTF-16 into ASCII: non-ASCII chars are mangled in
  47     // the process, but the resulting failure to parse a number is what
  48     // would have happened even after converting properly
  49     char buf[32];
  50     memset(buf, 0, sizeof(buf));
  51     WCHAR* src = argv[1];
  52     for (size_t i = 0; i < sizeof(buf) - 1; i++) {
  53         buf[i] = src[i];
  54     }
  55 
  56 
  57     int n = atoi(buf);
  58     if (n > 20) {
  59         return 20;
  60     }
  61     if (1 <= n && n <= 20) {
  62         return (size_t)n;
  63     }
  64     return fallback;
  65 }
  66 
  67 int WINAPI WinMain(HINSTANCE cur, HINSTANCE prev, LPSTR cl, int show_cmd) {
  68     MSG msg;
  69     const UINT kind = MB_OKCANCEL | MB_TASKMODAL | MB_ICONINFORMATION;
  70 
  71     size_t minutes = get_minutes();
  72     // make message string with the correct number of minutes
  73     char start_msg[strlen(start_fmt) + 20];
  74     char break_time_msg[strlen(break_time_fmt) + 20];
  75     sprintf(start_msg, start_fmt, (int)minutes);
  76     sprintf(break_time_msg, break_time_fmt, (int)minutes);
  77 
  78     // let the user quit before even starting
  79     if (MessageBoxA(NULL, start_msg, "Break Reminder", kind) != IDOK) {
  80         return msg.wParam;
  81     }
  82 
  83     // start first timer, or popup will never show up
  84     UINT_PTR timer = (UINT_PTR)NULL;
  85     SetTimer(NULL, timer, minutes * 60 * 1000, NULL);
  86 
  87     while (GetMessageW(&msg, NULL, 0, 0)) {
  88         switch (msg.message) {
  89         case WM_QUIT:
  90             return msg.wParam;
  91 
  92         case WM_TIMER:
  93             // stop timer, to ensure the time elapsed while popup is showing
  94             // doesn't count
  95             KillTimer(NULL, timer);
  96 
  97             // let the user quit any time the break-reminder is showing
  98             if (MessageBoxA(NULL, break_time_msg, "Time for a Break!", kind) != IDOK) {
  99                 return msg.wParam;
 100             }
 101 
 102             // start timer for the next popup
 103             SetTimer(NULL, timer, minutes * 60 * 1000, NULL);
 104             break;
 105 
 106         default:
 107             // handle generic events to play nice with the system
 108             TranslateMessage(&msg);
 109             DispatchMessage(&msg);
 110             break;
 111         }
 112     }
 113 
 114     return msg.wParam;
 115 }