alert_c.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #include "alert.h"
  2. // #include "os.h"
  3. #include <assert.h>
  4. #if defined(IS_MACOSX)
  5. #include <CoreFoundation/CoreFoundation.h>
  6. #elif defined(USE_X11)
  7. #include <stdio.h> /* For fputs() */
  8. #include <stdlib.h> /* For exit() */
  9. #include <sys/wait.h> /* For wait() */
  10. #include <unistd.h> /* For fork() */
  11. #include <sys/types.h> /* For pid_t */
  12. #include "../base/snprintf.h" /* For asprintf() */
  13. #endif
  14. #if defined(USE_X11)
  15. enum {
  16. TASK_SUCCESS = 0,
  17. FORK_FAILED = -1,
  18. EXEC_FAILED = -2
  19. };
  20. /*
  21. * Unfortunately, X has no standard method of displaying alerts, so instead we
  22. * have to rely on the shell command "xmessage" (or nicer-looking equivalents).
  23. *
  24. * The return value and arguments are the same as those from to runTask()
  25. * (see below).
  26. */
  27. static int xmessage(char *argv[], int *exit_status);
  28. #elif defined(IS_MACOSX)
  29. #define CFStringCreateWithUTF8String(string) \
  30. ((string) == NULL ? NULL : CFStringCreateWithCString(NULL, \
  31. string, \
  32. kCFStringEncodingUTF8))
  33. #endif
  34. int showAlert(const char *title, const char *msg, const char *defaultButton,
  35. const char *cancelButton)
  36. {
  37. #if defined(IS_MACOSX)
  38. CFStringRef alertHeader = CFStringCreateWithUTF8String(title);
  39. CFStringRef alertMessage = CFStringCreateWithUTF8String(msg);
  40. CFStringRef defaultButtonTitle = CFStringCreateWithUTF8String(defaultButton);
  41. CFStringRef cancelButtonTitle = CFStringCreateWithUTF8String(cancelButton);
  42. CFOptionFlags responseFlags;
  43. SInt32 err = CFUserNotificationDisplayAlert(0.0,
  44. kCFUserNotificationNoteAlertLevel,
  45. NULL,
  46. NULL,
  47. NULL,
  48. alertHeader,
  49. alertMessage,
  50. defaultButtonTitle,
  51. cancelButtonTitle,
  52. NULL,
  53. &responseFlags);
  54. if (alertHeader != NULL) CFRelease(alertHeader);
  55. if (alertMessage != NULL) CFRelease(alertMessage);
  56. if (defaultButtonTitle != NULL) CFRelease(defaultButtonTitle);
  57. if (cancelButtonTitle != NULL) CFRelease(cancelButtonTitle);
  58. if (err != 0) return -1;
  59. return (responseFlags == kCFUserNotificationDefaultResponse) ? 0 : 1;
  60. #elif defined(USE_X11)
  61. /* Note that args[0] is set by the xmessage() function. */
  62. const char *args[10] = {NULL, msg, "-title", title, "-center"};
  63. int response, ret;
  64. char *buttonList = NULL; /* To be free()'d. */
  65. if (defaultButton == NULL) defaultButton = "OK";
  66. if (cancelButton == NULL) {
  67. asprintf(&buttonList, "%s:2", defaultButton);
  68. } else {
  69. asprintf(&buttonList, "%s:2,%s:3", defaultButton, cancelButton);
  70. }
  71. if (buttonList == NULL) return -1; /* asprintf() failed. */
  72. args[5] = "-buttons";
  73. args[6] = buttonList;
  74. args[7] = "-default";
  75. args[8] = defaultButton;
  76. args[9] = NULL;
  77. ret = xmessage((char **)args, &response);
  78. if (buttonList != NULL) {
  79. free(buttonList);
  80. buttonList = NULL;
  81. }
  82. if (ret != TASK_SUCCESS) {
  83. if (ret == EXEC_FAILED) {
  84. fputs("xmessage or equivalent not found.\n", stderr);
  85. }
  86. return -1;
  87. }
  88. return (response == 2) ? 0 : 1;
  89. #else
  90. /* TODO: Display custom buttons instead of the pre-defined "OK"
  91. * and "Cancel". */
  92. int response = MessageBox(NULL, msg, title,
  93. (cancelButton == NULL) ? MB_OK : MB_OKCANCEL);
  94. return (response == IDOK) ? 0 : 1;
  95. #endif
  96. }
  97. #if defined(USE_X11)
  98. /*
  99. * Attempts to run the given task synchronously with the given arguments.
  100. *
  101. * If |exit_status| is non-NULL and the task ran successfully, |exit_status| is
  102. * set to the exit code of the task on return.
  103. *
  104. * Returns -1 if process could not be forked, -2 if the task could not be run,
  105. * or 0 if the task was ran successfully.
  106. */
  107. static int runTask(const char *taskname, char * const argv[], int *exit_status);
  108. static int xmessage(char *argv[], int *exit_status)
  109. {
  110. // static const char * const MSG_PROGS[] = {"gmessage", "gxmessage",
  111. // "kmessage", "xmessage"};
  112. static const char * const MSG_PROGS[] = {"xmessage"};
  113. static int PREV_MSG_INDEX = -1;
  114. #define MSG_PROGS_LEN (sizeof(MSG_PROGS) / sizeof(MSG_PROGS[0]))
  115. char *prog = NULL;
  116. int ret;
  117. /* Save some fork()'ing and attempt to use last program if possible. */
  118. if (PREV_MSG_INDEX >= 0) {
  119. assert(PREV_MSG_INDEX < MSG_PROGS_LEN);
  120. prog = argv[0] = (char *)MSG_PROGS[PREV_MSG_INDEX];
  121. ret = runTask(prog, argv, exit_status);
  122. } else {
  123. /* Otherwise, try running each xmessage alternative until one works or
  124. * we run out of options. */
  125. size_t i;
  126. for (i = 0; i < MSG_PROGS_LEN; ++i) {
  127. prog = argv[0] = (char *)MSG_PROGS[i];
  128. ret = runTask(prog, argv, exit_status);
  129. if (ret != EXEC_FAILED) break;
  130. }
  131. if (ret == TASK_SUCCESS) PREV_MSG_INDEX = i;
  132. }
  133. return ret;
  134. }
  135. static int runTask(const char *taskname, char * const argv[], int *exit_status)
  136. {
  137. pid_t pid = fork();
  138. int status;
  139. switch (pid) {
  140. case -1: /* Failed to fork */
  141. perror("fork");
  142. return FORK_FAILED; /* Failed to fork. */
  143. case 0: /* Child process */
  144. if (strcmp(argv[0],"xmessage") == 0){
  145. execvp(taskname, argv);
  146. perror("execvp failed");
  147. }
  148. exit(42); /* Failed to run task. */
  149. default: /* Parent process */
  150. wait(&status); /* Block execution until finished. */
  151. if (!WIFEXITED(status) || (status = WEXITSTATUS(status)) == 42) {
  152. return EXEC_FAILED; /* Task failed to run. */
  153. }
  154. if (exit_status != NULL) *exit_status = status;
  155. return TASK_SUCCESS; /* Success! */
  156. }
  157. }
  158. #endif