123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- #include "alert.h"
- // #include "os.h"
- #include <assert.h>
- #if defined(IS_MACOSX)
- #include <CoreFoundation/CoreFoundation.h>
- #elif defined(USE_X11)
- #include <stdio.h> /* For fputs() */
- #include <stdlib.h> /* For exit() */
- #include <sys/wait.h> /* For wait() */
- #include <unistd.h> /* For fork() */
- #include <sys/types.h> /* For pid_t */
- #include "../base/snprintf.h" /* For asprintf() */
- #endif
- #if defined(USE_X11)
- enum {
- TASK_SUCCESS = 0,
- FORK_FAILED = -1,
- EXEC_FAILED = -2
- };
- /*
- * Unfortunately, X has no standard method of displaying alerts, so instead we
- * have to rely on the shell command "xmessage" (or nicer-looking equivalents).
- *
- * The return value and arguments are the same as those from to runTask()
- * (see below).
- */
- static int xmessage(char *argv[], int *exit_status);
- #elif defined(IS_MACOSX)
- #define CFStringCreateWithUTF8String(string) \
- ((string) == NULL ? NULL : CFStringCreateWithCString(NULL, \
- string, \
- kCFStringEncodingUTF8))
- #endif
- int showAlert(const char *title, const char *msg, const char *defaultButton,
- const char *cancelButton)
- {
- #if defined(IS_MACOSX)
- CFStringRef alertHeader = CFStringCreateWithUTF8String(title);
- CFStringRef alertMessage = CFStringCreateWithUTF8String(msg);
- CFStringRef defaultButtonTitle = CFStringCreateWithUTF8String(defaultButton);
- CFStringRef cancelButtonTitle = CFStringCreateWithUTF8String(cancelButton);
- CFOptionFlags responseFlags;
- SInt32 err = CFUserNotificationDisplayAlert(0.0,
- kCFUserNotificationNoteAlertLevel,
- NULL,
- NULL,
- NULL,
- alertHeader,
- alertMessage,
- defaultButtonTitle,
- cancelButtonTitle,
- NULL,
- &responseFlags);
- if (alertHeader != NULL) CFRelease(alertHeader);
- if (alertMessage != NULL) CFRelease(alertMessage);
- if (defaultButtonTitle != NULL) CFRelease(defaultButtonTitle);
- if (cancelButtonTitle != NULL) CFRelease(cancelButtonTitle);
- if (err != 0) return -1;
- return (responseFlags == kCFUserNotificationDefaultResponse) ? 0 : 1;
- #elif defined(USE_X11)
- /* Note that args[0] is set by the xmessage() function. */
- const char *args[10] = {NULL, msg, "-title", title, "-center"};
- int response, ret;
- char *buttonList = NULL; /* To be free()'d. */
- if (defaultButton == NULL) defaultButton = "OK";
- if (cancelButton == NULL) {
- asprintf(&buttonList, "%s:2", defaultButton);
- } else {
- asprintf(&buttonList, "%s:2,%s:3", defaultButton, cancelButton);
- }
- if (buttonList == NULL) return -1; /* asprintf() failed. */
- args[5] = "-buttons";
- args[6] = buttonList;
- args[7] = "-default";
- args[8] = defaultButton;
- args[9] = NULL;
- ret = xmessage((char **)args, &response);
- if (buttonList != NULL) {
- free(buttonList);
- buttonList = NULL;
- }
- if (ret != TASK_SUCCESS) {
- if (ret == EXEC_FAILED) {
- fputs("xmessage or equivalent not found.\n", stderr);
- }
- return -1;
- }
- return (response == 2) ? 0 : 1;
- #else
- /* TODO: Display custom buttons instead of the pre-defined "OK"
- * and "Cancel". */
- int response = MessageBox(NULL, msg, title,
- (cancelButton == NULL) ? MB_OK : MB_OKCANCEL);
- return (response == IDOK) ? 0 : 1;
- #endif
- }
- #if defined(USE_X11)
- /*
- * Attempts to run the given task synchronously with the given arguments.
- *
- * If |exit_status| is non-NULL and the task ran successfully, |exit_status| is
- * set to the exit code of the task on return.
- *
- * Returns -1 if process could not be forked, -2 if the task could not be run,
- * or 0 if the task was ran successfully.
- */
- static int runTask(const char *taskname, char * const argv[], int *exit_status);
- static int xmessage(char *argv[], int *exit_status)
- {
- // static const char * const MSG_PROGS[] = {"gmessage", "gxmessage",
- // "kmessage", "xmessage"};
- static const char * const MSG_PROGS[] = {"xmessage"};
- static int PREV_MSG_INDEX = -1;
- #define MSG_PROGS_LEN (sizeof(MSG_PROGS) / sizeof(MSG_PROGS[0]))
- char *prog = NULL;
- int ret;
- /* Save some fork()'ing and attempt to use last program if possible. */
- if (PREV_MSG_INDEX >= 0) {
- assert(PREV_MSG_INDEX < MSG_PROGS_LEN);
- prog = argv[0] = (char *)MSG_PROGS[PREV_MSG_INDEX];
- ret = runTask(prog, argv, exit_status);
- } else {
- /* Otherwise, try running each xmessage alternative until one works or
- * we run out of options. */
- size_t i;
- for (i = 0; i < MSG_PROGS_LEN; ++i) {
- prog = argv[0] = (char *)MSG_PROGS[i];
- ret = runTask(prog, argv, exit_status);
- if (ret != EXEC_FAILED) break;
- }
- if (ret == TASK_SUCCESS) PREV_MSG_INDEX = i;
- }
- return ret;
- }
- static int runTask(const char *taskname, char * const argv[], int *exit_status)
- {
- pid_t pid = fork();
- int status;
- switch (pid) {
- case -1: /* Failed to fork */
- perror("fork");
- return FORK_FAILED; /* Failed to fork. */
- case 0: /* Child process */
- if (strcmp(argv[0],"xmessage") == 0){
- execvp(taskname, argv);
- perror("execvp failed");
- }
- exit(42); /* Failed to run task. */
- default: /* Parent process */
- wait(&status); /* Block execution until finished. */
- if (!WIFEXITED(status) || (status = WEXITSTATUS(status)) == 42) {
- return EXEC_FAILED; /* Task failed to run. */
- }
- if (exit_status != NULL) *exit_status = status;
- return TASK_SUCCESS; /* Success! */
- }
- }
- #endif
|