From a9bd86ab3b1e028ab03425ac28f00b4d451684b8 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 22 Apr 2005 13:25:16 +0000 Subject: [PATCH] Did a lot of ripping things up. Now all the UI drivers are compiled for all platforms (and nonsense ones, like ui_carbon.c on, say, Windows, will be one-fuction stubs), and the appropriate one is chosen at startup. This lets us pick a GUI one by default, but fall back to stdio if there's an initialization issue or the let the user pick stdio because he's ssh'd to a headless server, etc). It also lets us plug in a Qt and GTK and blahblahblah UI on linux and let everything coexist in one binary. Also, a lot of the bad abstraction breakage in platform_unix.c that got wedged in there when this was a Mac Thing Done In A Hurry has been cleaned up into general application code in mojopatch.c, platform stuff in platform_*, and UI stuff in the UI drivers. Over all, this was a lot of good cleanup that'll make sane Linux and Windows ports much easier. --- Makefile | 14 +-- TODO | 1 - mojopatch.c | 108 +++++++++++++++++++++-- platform.h | 6 +- platform_unix.c | 193 +++++++++++++---------------------------- ui.c | 100 ++++++++++++++++++++++ ui.h | 53 +++++++++--- ui_carbon.c | 222 +++++++++++++++++++++++++----------------------- ui_stdio.c | 112 ++++++++++++++++++------ 9 files changed, 512 insertions(+), 297 deletions(-) create mode 100644 ui.c diff --git a/Makefile b/Makefile index a1e4aab..e5d8192 100644 --- a/Makefile +++ b/Makefile @@ -20,19 +20,19 @@ use_pthread := false # you probably shouldn't touch anything below this line. ifeq ($(strip $(platform)),macosx) -PLATFORMDEF := -DPLATFORM_UNIX -DPLATFORM_MACOSX -PLATFORMSRCS := platform_unix.c ui_carbon.c +PLATFORMDEF := -DPLATFORM_UNIX=1 -DPLATFORM_MACOSX=1 +PLATFORMSRCS := platform_unix.c LDFLAGS := -framework Carbon endif ifeq ($(strip $(platform)),win32) -PLATFORMDEF := -DPLATFORM_WIN32 -PLATFORMSRCS := platform_win32.c ui_stdio.c +PLATFORMDEF := -DPLATFORM_WIN32=1 +PLATFORMSRCS := platform_win32.c endif ifeq ($(strip $(platform)),unix) - PLATFORMDEF := -DPLATFORM_UNIX - PLATFORMSRCS := platform_unix.c ui_stdio.c + PLATFORMDEF := -DPLATFORM_UNIX=1 + PLATFORMSRCS := platform_unix.c endif #CFLAGS := $(PLATFORMDEF) -Wall -g -fsigned-char -fno-omit-frame-pointer -O0 -DDEBUG=1 -D_DEBUG=1 @@ -54,7 +54,7 @@ ifeq ($(strip $(use_pthread)),true) endif endif -MOJOPATCHSRCS := mojopatch.c md5.c $(PLATFORMSRCS) +MOJOPATCHSRCS := mojopatch.c md5.c ui.c ui_carbon.c ui_stdio.c $(PLATFORMSRCS) OBJS1 := $(MOJOPATCHSRCS:.c=.o) OBJS2 := $(OBJS1:.cpp=.o) OBJS3 := $(OBJS2:.asm=.o) diff --git a/TODO b/TODO index 1715dbe..03697bb 100644 --- a/TODO +++ b/TODO @@ -10,7 +10,6 @@ - Get other platforms besides Mac updated and building again. - Look for FIXMEs... - "_fatal" isn't really appropriate anymore, since it might not be fatal. -- chdir_by_identifier() has way too many unwritten responsibilities. - We md5sum to compare files. A byte-by-byte compare that halts as soon as there's a difference would be much faster when creating the initial patch files. diff --git a/mojopatch.c b/mojopatch.c index 373d2fc..835c5d4 100644 --- a/mojopatch.c +++ b/mojopatch.c @@ -2022,6 +2022,80 @@ static inline void header_log(const char *str, const char *val) } /* header_log */ +static int show_and_install_readme(const char *fname, const char *text) +{ + FILE *io = fopen(fname, "wb"); + if (io == NULL) + { + _fatal("Failed to open [%s] for writing.", fname); + return(0); + } /* if */ + + /* !!! FIXME: "text" may be binary data, not an asciz string... */ + fputs(text, io); /* !!! FIXME: error checking! */ + fclose(io); + return(ui_show_readme(fname, text)); +} /* show_and_install_readme */ + + +static int manually_locate_product(const char *name, char *buf, size_t bufsize) +{ + const char *promptfmt = "We can't find your \"%s\" installation." + " Would you like to show us where it is?"; + char *promptstr = alloca(strlen(name) + strlen(promptfmt) + 1); + + if (promptstr == NULL) + { + _fatal("Out of memory."); + return(0); + } /* if */ + sprintf(promptstr, promptfmt, name); + + if (!ui_prompt_yn(promptstr)) + { + _log("User chose not to manually locate installation"); + return(0); + } /* if */ + + return(ui_file_picker(buf, bufsize)); +} /* manually_locate_product */ + + +static int chdir_by_identifier(const char *name, const char *str, + const char *version, const char *newversion) +{ + char buf[MAXPATHLEN]; + int hasident = ((str != NULL) && (*str)); + int found = 0; + + if (hasident) + { + found = locate_product_by_identifier(str, buf, sizeof (buf)); + if (!found) + _log("Couldn't find product. Perhaps it isn't installed?"); + } /* if */ + + if (!found) /* No identifier, or platform layer couldn't find it. */ + { + if (!manually_locate_product(name, buf, sizeof (buf))) + { + _fatal("We can't patch the product if we can't find it!"); + return(0); + } /* if */ + } /* if */ + + _log("I think the product is installed at [%s].", buf); + + if (chdir(buf) != 0) + { + _fatal("Failed to change to product's installation directory."); + return(0); + } /* if */ + + return(check_product_version(str, version, newversion)); +} /* chdir_by_identifier */ + + static int process_patch_header(SerialArchive *ar, PatchHeader *h) { int retval = PATCHSUCCESS; @@ -2255,6 +2329,8 @@ static int parse_cmdline(int argc, char **argv) make_static_string(header.renamedir, argv[++i]); else if (strcmp(argv[i], "--titlebar") == 0) make_static_string(header.titlebar, argv[++i]); + else if (strcmp(argv[i], "--ui") == 0) + i++; /* (really handled elsewhere.) Just skip ui driver name. */ else if (strcmp(argv[i], "--zliblevel") == 0) { zliblevel = atoi(argv[++i]); @@ -2339,6 +2415,31 @@ static int parse_cmdline(int argc, char **argv) /* !!! FIXME: signal_cleanup */ +static int kickoff_ui(int argc, char **argv) +{ + int seen_ui = 0; + int i; + + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--ui") == 0) + { + seen_ui = 1; + if (ui_init(argv[++i])) + return(1); + } /* if */ + } /* for */ + + if (!seen_ui) + { + if (ui_init(NULL)) + return(1); + } /* if */ + + fprintf(stderr, "MojoPatch: ui_init() failed!"); /* oh well. */ + return(0); +} /* kickoff_ui */ + int mojopatch_main(int argc, char **argv) { @@ -2347,11 +2448,8 @@ int mojopatch_main(int argc, char **argv) memset(&header, '\0', sizeof (header)); - if (!ui_init()) - { - _fatal("MojoPatch: ui_init() failed!"); /* oh well. */ - return(PATCHERROR); - } /* if */ + if (!kickoff_ui(argc, argv)) + return(PATCHERROR); /* oh well. */ _log("MojoPatch %s starting up.", VERSION); diff --git a/platform.h b/platform.h index 74a9107..662135c 100644 --- a/platform.h +++ b/platform.h @@ -49,6 +49,7 @@ void _log(const char *fmt, ...); /* Call this for logging (debug info). */ void _dlog(const char *fmt, ...); +/* Does a given version match the requirements? */ int version_ok(const char *ver, const char *allowed, const char *newver); /* platform-specific stuff you implement. */ @@ -62,9 +63,8 @@ char *get_realpath(const char *path); int spawn_xdelta(const char *cmdline); int update_version(const char *ver); int calc_tmp_filenames(char **tmp1, char **tmp2); -int show_and_install_readme(const char *fname, const char *text); -int chdir_by_identifier(const char *name, const char *str, - const char *ver, const char *newver); +int locate_product_by_identifier(const char *str, char *buf, size_t bufsize); +int check_product_version(const char *ident, const char *version, const char *newversion); #ifdef __cplusplus } diff --git a/platform_unix.c b/platform_unix.c index 8ff67f6..29cc420 100644 --- a/platform_unix.c +++ b/platform_unix.c @@ -169,7 +169,7 @@ char *get_realpath(const char *path) } /* get_realpath */ -#ifdef PLATFORM_MACOSX +#if PLATFORM_MACOSX #include static char *parse_xml(char *ptr, char **tag, char **val) @@ -287,12 +287,13 @@ static char *find_info_plist_bundle_id(char *ptr) return(NULL); } /* find_info_plist_version */ +#endif /* PLATFORM_MACOSX */ -static int parse_info_dot_plist(const char *ident, - const char *version, - const char *newversion) +int check_product_version(const char *ident, const char *version, + const char *newversion) { +#if PLATFORM_MACOSX const char *fname = "Contents/Info.plist"; /* already chdir'd for this. */ char *mem = NULL; char *ptr; @@ -309,20 +310,24 @@ static int parse_info_dot_plist(const char *ident, io = NULL; mem[fsize] = '\0'; - ptr = find_info_plist_bundle_id(mem); - if ((ptr == NULL) || (strcasecmp(ptr, ident) != 0)) + if (ident != NULL) { - int yes = ui_prompt_ny("We don't think we're looking at the right directory!" - " Are you SURE this is the right place?" - " If you aren't sure, clicking 'Yes' can destroy unrelated files!"); - if (!yes) + ptr = find_info_plist_bundle_id(mem); + if ((ptr == NULL) || (strcasecmp(ptr, ident) != 0)) { - _fatal("Stopping at user's request."); - free(mem); - return(0); + int yes = ui_prompt_ny("We don't think we're looking at the right directory!" + " Are you SURE this is the right place?" + " If you aren't sure, clicking 'Yes' can destroy unrelated files!"); + if (!yes) + { + _fatal("Stopping at user's request."); + free(mem); + return(0); + } /* if */ } /* if */ } /* if */ + /* !!! FIXME: this is kinda a lame hack. */ if ( (io = fopen(fname, "r")) == NULL ) goto parse_info_plist_bailed; if ( (fread(mem, fsize, 1, io)) != 1 ) goto parse_info_plist_bailed; fclose(io); @@ -348,11 +353,48 @@ static int parse_info_dot_plist(const char *ident, if (!knowver) _fatal("Can't determine product's installed version."); return(retval); +#else + _fatal("Not implemented!"); /* !!! FIXME */ + return(0); +#endif } /* parse_info_dot_plist */ +int locate_product_by_identifier(const char *str, char *buf, size_t bufsize) +{ +#if PLATFORM_MACOSX + /* Ask LaunchServices to find product by identifier... */ + OSStatus rc; + CFURLRef url = NULL; + CFStringRef id = CFStringCreateWithBytes(NULL, str, strlen(str), + kCFStringEncodingISOLatin1, 0); + + /* + * !!! FIXME: This has a tendency to find installs in the trashcan. :/ + * !!! FIXME: It might be worth checking if the product install has + * !!! FIXME: a dir named ".Trash" in the hierarchy, and considering + * !!! FIXME: it not found if so. + */ + rc = LSFindApplicationForInfo(kLSUnknownCreator, id, NULL, NULL, &url); + CFRelease(id); + if (rc == noErr) + { + Boolean b = CFURLGetFileSystemRepresentation(url, TRUE, buf, bufsize); + CFRelease(url); + return(b != 0); + } /* if */ + + return(0); +#else + _fatal("Not implemented!"); /* !!! FIXME */ + return(0); +#endif +} /* locate_product_by_identifier */ + + int update_version(const char *ver) { +#if PLATFORM_MACOSX const char *fname = "Contents/Info.plist"; /* already chdir'd for this. */ char *mem = NULL; char *ptr; @@ -390,139 +432,18 @@ int update_version(const char *ver) if (!retval) _fatal("Can't update product's installed version."); return(retval); -} /* update_version */ - - -int manually_locate_product(const char *name, char *buf, size_t bufsize); - -int chdir_by_identifier(const char *name, const char *str, - const char *version, const char *newversion) -{ - char buf[MAXPATHLEN]; - Boolean b; - OSStatus rc; - int found = 0; - int hasident = ((str != NULL) && (*str)); - - /* if an identifier is specified, ask LaunchServices to find product... */ - if (hasident) - { - CFURLRef url = NULL; - CFStringRef id = CFStringCreateWithBytes(NULL, str, strlen(str), - kCFStringEncodingISOLatin1, 0); - - rc = LSFindApplicationForInfo(kLSUnknownCreator, id, NULL, NULL, &url); - CFRelease(id); - if (rc == noErr) - { - b = CFURLGetFileSystemRepresentation(url, TRUE, buf, sizeof (buf)); - CFRelease(url); - if (!b) - { - _fatal("Internal error."); - return(0); - } /* if */ - found = 1; - } /* if */ - else - { - _log("Couldn't find product. Perhaps it isn't installed?"); - } /* if */ - } /* if */ - - if (!found) /* No identifier, or LaunchServices couldn't find it. */ - { - if (!manually_locate_product(name, buf, sizeof (buf))) - { - _fatal("We can't patch the product if we can't find it!"); - return(0); - } /* if */ - } /* if */ - - _log("I think the product is installed at [%s].", buf); - - if (chdir(buf) != 0) - { - _fatal("Failed to change to product's installation directory."); - return(0); - } /* if */ - - if (hasident) - return(parse_info_dot_plist(str, version, newversion)); - - return(1); -} /* chdir_by_identifier */ - - -int show_and_install_readme(const char *fname, const char *text) -{ - FILE *io; - char *cmd; - const char *envr = getenv("HOME"); - if (!envr) - { - _fatal("HOME environment var not set?"); - return(0); - } /* if */ - - cmd = alloca(strlen(fname) + strlen(envr) + 30); - strcpy(cmd, "open "); - strcat(cmd, envr); - if (cmd[strlen(cmd)-1] != '/') - strcat(cmd, "/"); - strcat(cmd, "Desktop/"); - strcat(cmd, fname); - - io = fopen(cmd + 5, "w"); - if (!io) - { - _fatal("Failed to open [%s] for writing.", cmd+5); - return(0); - } /* if */ - - /* !!! FIXME: error checking! */ - fputs(text, io); - fclose(io); - system(cmd); - return(1); -} /* show_and_install_readme */ - #else /* Regular old POSIX-compliant Unix... */ -int update_version(const char *ver) -{ /* * !!! FIXME: need some way to flag this install as updated... * !!! FIXME: maybe just leave unimplemented? */ _fatal("Not implemented!"); return(0); -} /* show_and_install_readme */ - -int show_and_install_readme(const char *fname, const char *text) -{ - /* - * !!! FIXME: Can just dump to stdout? This should really be in the - * !!! FIXME: UI modules... - */ - _fatal("Not implemented!"); - return(0); -} /* show_and_install_readme */ - - -int chdir_by_identifier(const char *name, const char *str, - const char *version, const char *newversion) -{ - /* - * !!! FIXME: need some way to find the program automatically... - * !!! FIXME: maybe just prompt the user? Oh well. - */ - _fatal("Not implemented!"); - return(0); -} /* chdir_by_identifier */ #endif +} /* update_version */ int calc_tmp_filenames(char **tmp1, char **tmp2) @@ -569,7 +490,7 @@ int spawn_xdelta(const char *cmdline) return(0); } /* if */ - else if (pid == 0) // child process. + else if (pid == 0) /* child process. */ { rc = spawn_thread(cmd); exit(1); /* !!! FIXME *((int *) rc) == 0 ); */ diff --git a/ui.c b/ui.c new file mode 100644 index 0000000..fbf0b6c --- /dev/null +++ b/ui.c @@ -0,0 +1,100 @@ + +#include +#include +#include +#include +#include "platform.h" +#include "ui.h" + +/* stubs for when a driver isn't selected. */ +static void null_ui(void) { assert(0 && "UI call without UI driver!"); } +static void ui_pump_null(void) { null_ui(); } +static void ui_add_to_log_null(const char *str, int debugging) { null_ui(); } +static void ui_fatal_null(const char *str) { null_ui(); } +static void ui_success_null(const char *str) { null_ui(); } +static void ui_msgbox_null(const char *str) { null_ui(); } +static void ui_total_progress_null(int percent) { null_ui(); } +static void ui_status_null(const char *str) { null_ui(); } +static void ui_title_null(const char *str) { null_ui(); } +static int ui_prompt_yn_null(const char *question) { null_ui(); return(0); } +static int ui_prompt_ny_null(const char *question) { null_ui(); return(0); } +static void ui_real_deinit_null(void) { null_ui(); } +static int ui_file_picker_null(char *buf, size_t bufsize) { null_ui(); return(0); } +static int ui_show_readme_null(const char *fname,const char *text) { null_ui(); return(0); } + + +static int ui_selected = 0; + +int ui_init_carbon(void); +int ui_init_stdio(void); + +typedef struct +{ + const char *driver_name; + int (*initfunc)(void); +} UIDrivers; + +static UIDrivers ui_drivers[] = +{ + { "carbon", ui_init_carbon }, + { "stdio", ui_init_stdio }, /* should probably always be last. */ + { NULL, NULL } +}; + +int ui_init(const char *request_driver) +{ + int i; + + if (ui_selected) + return(1); + + /* make sure we're in a sane state... */ + UI_SET_FUNC_POINTERS(null); + + if (request_driver != NULL) + { + for (i = 0; ui_drivers[i].initfunc != NULL; i++) + { + if (strcmp(ui_drivers[i].driver_name, request_driver) == 0) + { + ui_selected = ui_drivers[i].initfunc(); + break; + } /* if */ + } /* for */ + } /* if */ + + for (i = 0; (!ui_selected) && (ui_drivers[i].initfunc != NULL); i++) + ui_selected = ui_drivers[i].initfunc(); + + return(ui_selected); +} /* ui_init */ + + +void ui_deinit(void) +{ + if (!ui_selected) + return; + + ui_real_deinit(); + UI_SET_FUNC_POINTERS(null); + ui_selected = 0; +} /* ui_deinit */ + + +/* The driver function pointers... */ +void (*ui_real_deinit)(void) = ui_real_deinit_null; +void (*ui_pump)(void) = ui_pump_null; +void (*ui_add_to_log)(const char *str, int debugging) = ui_add_to_log_null; +void (*ui_fatal)(const char *str) = ui_fatal_null; +void (*ui_success)(const char *str) = ui_success_null; +void (*ui_msgbox)(const char *str) = ui_msgbox_null; +void (*ui_total_progress)(int percent) = ui_total_progress_null; +void (*ui_status)(const char *str) = ui_status_null; +void (*ui_title)(const char *str) = ui_title_null; +int (*ui_prompt_yn)(const char *question) = ui_prompt_yn_null; +int (*ui_prompt_ny)(const char *question) = ui_prompt_ny_null; +int (*ui_file_picker)(char *buf, size_t bufsize) = ui_file_picker_null; +int (*ui_show_readme)(const char *fname,const char *text)=ui_show_readme_null; + +/* end of ui.c ... */ + diff --git a/ui.h b/ui.h index f52dffc..956bd5d 100644 --- a/ui.h +++ b/ui.h @@ -6,19 +6,48 @@ extern "C" { #endif -/* user interface stuff you implement. */ -int ui_init(void); +/* user interface stuff you implement: see ui.c and ui_stdio.c for examples */ +int ui_init(const char *request_driver); void ui_deinit(void); -void ui_pump(void); -void ui_add_to_log(const char *str, int debugging); -void ui_fatal(const char *str); -void ui_success(const char *str); -void ui_msgbox(const char *str); -void ui_total_progress(int percent); -void ui_status(const char *str); -void ui_title(const char *str); -int ui_prompt_yn(const char *question); -int ui_prompt_ny(const char *question); +extern void (*ui_pump)(void); +extern void (*ui_add_to_log)(const char *str, int debugging); +extern void (*ui_fatal)(const char *str); +extern void (*ui_success)(const char *str); +extern void (*ui_msgbox)(const char *str); +extern void (*ui_total_progress)(int percent); +extern void (*ui_status)(const char *str); +extern void (*ui_title)(const char *str); +extern int (*ui_prompt_yn)(const char *question); +extern int (*ui_prompt_ny)(const char *question); +extern int (*ui_file_picker)(char *buf, size_t bufsize); +extern int (*ui_show_readme)(const char *fname, const char *text); + +/* this is for the UI layer, the application uses ui_deinit() instead... */ +extern void (*ui_real_deinit)(void); + +/* + * Macros for use by the UI layer. The application should ignore this. + * + * (This all feels a little naughty, but it guarantees we'll catch it when a + * a new entry point is added and a driver hasn't been updated.) + */ +#define UI_SET_FUNC_POINTER(func,drv) { func = func##_##drv; } +#define UI_SET_FUNC_POINTERS(drv) \ +{ \ + UI_SET_FUNC_POINTER(ui_real_deinit,drv) \ + UI_SET_FUNC_POINTER(ui_pump,drv) \ + UI_SET_FUNC_POINTER(ui_add_to_log,drv) \ + UI_SET_FUNC_POINTER(ui_fatal,drv) \ + UI_SET_FUNC_POINTER(ui_success,drv) \ + UI_SET_FUNC_POINTER(ui_msgbox,drv) \ + UI_SET_FUNC_POINTER(ui_total_progress,drv) \ + UI_SET_FUNC_POINTER(ui_status,drv) \ + UI_SET_FUNC_POINTER(ui_title,drv) \ + UI_SET_FUNC_POINTER(ui_prompt_yn,drv) \ + UI_SET_FUNC_POINTER(ui_prompt_ny,drv) \ + UI_SET_FUNC_POINTER(ui_file_picker,drv) \ + UI_SET_FUNC_POINTER(ui_show_readme,drv) \ +} #ifdef __cplusplus } diff --git a/ui_carbon.c b/ui_carbon.c index 125373c..696688c 100644 --- a/ui_carbon.c +++ b/ui_carbon.c @@ -1,3 +1,8 @@ + +#if !PLATFORM_MACOSX +int ui_init_carbon(void) { return(0); } /* not implemented if not MacOS. */ +#else + #include #include "platform.h" @@ -10,95 +15,47 @@ static WindowRef window; static ControlRef progress; static ControlRef status; -static int carbon_ui_initialized = 0; -/* user interface stuff you implement. */ -int ui_init(void) +static void ui_pump_carbon(void) { - ControlID statusID = { MOJOPATCH_SIG, MOJOPATCH_STATUS_ID }; - ControlID progressID = { MOJOPATCH_SIG, MOJOPATCH_PROGRESS_ID }; - IBNibRef nibRef = NULL; - OSStatus err; - Boolean b = TRUE; - - if (carbon_ui_initialized) /* already initialized? */ - return(1); - - /* !!! FIXME: This is corrupting the "basedir" variable in platform_unix.c ! */ - if (CreateNibReference(CFSTR("mojopatch"), &nibRef) != noErr) - { - fprintf(stderr, "MOJOPATCH: You probably don't have a .nib file!\n"); - return(0); /* usually .nib isn't found. */ - } /* if */ - - err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar")); - if (err == noErr) - err = CreateWindowFromNib(nibRef, CFSTR("MainWindow"), &window); - DisposeNibReference( nibRef ); - - if (err == noErr) - err = GetControlByID(window, &statusID, &status); - - if (err == noErr) - err = GetControlByID(window, &progressID, &progress); - - if (err == noErr) - { - ShowWindow(window); - err = ActivateWindow(window, TRUE); - } /* if */ + EventRef theEvent; + EventTargetRef theTarget; - if (err == noErr) + theTarget = GetEventDispatcherTarget(); + if (ReceiveNextEvent(0, NULL, 0, true, &theEvent) == noErr) { - err = SetControlData(progress, kControlEntireControl, - kControlProgressBarAnimatingTag, - sizeof (b), &b); + SendEventToEventTarget(theEvent, theTarget); + ReleaseEvent(theEvent); } /* if */ - - carbon_ui_initialized = 1; - return(err == noErr); -} /* ui_init */ +} /* ui_pump_carbon */ -void ui_title(const char *str) +static void ui_title_carbon(const char *str) { CFStringRef cfstr = CFStringCreateWithBytes(NULL, str, strlen(str), kCFStringEncodingISOLatin1, 0); SetWindowTitleWithCFString(window, cfstr); CFRelease(cfstr); - ui_pump(); -} /* ui_title */ + ui_pump_carbon(); +} /* ui_title_carbon */ -void ui_deinit(void) +static void ui_real_deinit_carbon(void) { /* !!! FIXME */ - /* carbon_ui_initialized = 0; */ -} /* ui_deinit */ +} /* ui_real_deinit_carbon */ -void ui_pump(void) +static void ui_add_to_log_carbon(const char *str, int debugging) { - EventRef theEvent; - EventTargetRef theTarget; - - if (!carbon_ui_initialized) - return; + /* + * stdout in a Mac GUI app shows up in the system console, which can be + * viewed via /Applications/Utilities/Console.app ... + */ - theTarget = GetEventDispatcherTarget(); - if (ReceiveNextEvent(0, NULL, 0, true, &theEvent) == noErr) - { - SendEventToEventTarget(theEvent, theTarget); - ReleaseEvent(theEvent); - } /* if */ -} /* ui_pump */ - - -void ui_add_to_log(const char *str, int debugging) -{ /* !!! FIXME */ printf("MojoPatch%s: %s\n", debugging ? " [debug]" : "", str); -} /* ui_add_to_log */ +} /* ui_add_to_log_carbon */ static int do_msgbox(const char *str, AlertType alert_type, @@ -155,18 +112,20 @@ static int ui_prompt_yes_or_no(const char *question, int yes) return(item == kAlertStdAlertOKButton); } /* ui_prompt_yes_or_no */ -int ui_prompt_yn(const char *question) + +static int ui_prompt_yn_carbon(const char *question) { return(ui_prompt_yes_or_no(question, 1)); -} /* ui_prompt_yn */ +} /* ui_prompt_yn_carbon */ -int ui_prompt_ny(const char *question) + +static int ui_prompt_ny_carbon(const char *question) { return(ui_prompt_yes_or_no(question, 1)); /* !!! FIXME! should be zero. */ -} /* ui_prompt_ny */ +} /* ui_prompt_ny_carbon */ -int manually_locate_product(const char *name, char *buf, size_t bufsize) +static int ui_file_picker_carbon(char *buf, size_t bufsize) { NavDialogCreationOptions dlgopt; NavDialogRef dlg; @@ -177,30 +136,12 @@ int manually_locate_product(const char *name, char *buf, size_t bufsize) FSRef fsref; OSStatus rc; int retval = 0; - int yn; - const char *promptfmt = "We can't find your \"%s\" installation." - " Would you like to show us where it is?"; - char *promptstr = alloca(strlen(name) + strlen(promptfmt) + 1); - - if (promptstr == NULL) - { - _fatal("Out of memory."); - return(0); - } /* if */ - sprintf(promptstr, promptfmt, name); - - yn = ui_prompt_yn(promptstr); - if (!yn) - { - _log("User chose not to manually locate installation"); - return(0); - } /* if */ NavGetDefaultDialogCreationOptions(&dlgopt); dlgopt.optionFlags |= kNavSupportPackages; dlgopt.optionFlags |= kNavAllowOpenPackages; dlgopt.optionFlags &= ~kNavAllowMultipleFiles; - dlgopt.windowTitle = CFSTR("Please select the product's icon and click 'OK'."); + dlgopt.windowTitle = CFSTR("Please select the product's icon and click 'OK'."); /* !!! FIXME! */ dlgopt.actionButtonLabel = CFSTR("OK"); NavCreateChooseFolderDialog(&dlgopt, NULL, NULL, NULL, &dlg); NavDialogRun(dlg); @@ -212,7 +153,7 @@ int manually_locate_product(const char *name, char *buf, size_t bufsize) NavDialogGetReply(dlg, &reply); rc = AEGetNthDesc(&reply.selection, 1, typeFSRef, &keyword, &desc); if (rc != noErr) - _fatal("Unexpected error in AEGetNthDesc: %d\n", (int) rc); + _fatal("Unexpected error in AEGetNthDesc: %d", (int) rc); else { /* !!! FIXME: Check return values here! */ @@ -232,31 +173,28 @@ int manually_locate_product(const char *name, char *buf, size_t bufsize) retval ? "selected" : "did NOT select"); return(retval); -} /* manually_locate_product */ +} /* ui_file_picker_carbon */ -void ui_fatal(const char *str) +static void ui_fatal_carbon(const char *str) { - if (!carbon_ui_initialized) - fprintf(stderr, "FATAL ERROR: %s\n", str); - else - do_msgbox(str, kAlertStopAlert, NULL, NULL); + do_msgbox(str, kAlertStopAlert, NULL, NULL); } /* ui_fatal */ -void ui_success(const char *str) +static void ui_success_carbon(const char *str) { do_msgbox(str, kAlertNoteAlert, NULL, NULL); -} /* ui_success */ +} /* ui_success_carbon */ -void ui_msgbox(const char *str) +static void ui_msgbox_carbon(const char *str) { do_msgbox(str, kAlertNoteAlert, NULL, NULL); -} /* ui_msgbox */ +} /* ui_msgbox_carbon */ -void ui_total_progress(int percent) +static void ui_total_progress_carbon(int percent) { static int lastpercent = -1; if (percent != lastpercent) @@ -268,15 +206,87 @@ void ui_total_progress(int percent) SetControl32BitValue(progress, percent); lastpercent = percent; } /* if */ -} /* ui_total_progress */ +} /* ui_total_progress_carbon */ -void ui_status(const char *str) +static void ui_status_carbon(const char *str) { SetControlData(status, kControlEditTextPart, kControlStaticTextTextTag, strlen(str), str); Draw1Control(status); -} /* ui_status */ +} /* ui_status_carbon */ + + +static int ui_show_readme_carbon(const char *fname, const char *text) +{ + /* + * Just let the Finder pick the right program to view the file... + * this lets you ship with a .txt, .html, .rtf, or whatever, for a + * readme on this platform. + */ + + size_t allocsize = strlen(fname) + 32; + char *cmd = (char *) alloca(allocsize); + if (!cmd) + { + _fatal("Out of memory."); + return(0); + } /* if */ + + snprintf(cmd, allocsize, "open %s", fname); + system(cmd); /* !!! FIXME: error check? */ + return(1); +} /* ui_show_readme_carbon */ + + +/* user interface stuff you implement. */ +int ui_init_carbon(void) +{ + ControlID statusID = { MOJOPATCH_SIG, MOJOPATCH_STATUS_ID }; + ControlID progressID = { MOJOPATCH_SIG, MOJOPATCH_PROGRESS_ID }; + IBNibRef nibRef = NULL; + OSStatus err; + Boolean b = TRUE; + + /* !!! FIXME: This is corrupting the "basedir" variable in platform_unix.c ! */ + if (CreateNibReference(CFSTR("mojopatch"), &nibRef) != noErr) + { + fprintf(stderr, "MOJOPATCH: Carbon UI failed to initialize!\n"); + fprintf(stderr, "MOJOPATCH: You probably don't have a .nib file!\n"); + return(0); /* usually .nib isn't found. */ + } /* if */ + + err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar")); + if (err == noErr) + err = CreateWindowFromNib(nibRef, CFSTR("MainWindow"), &window); + DisposeNibReference( nibRef ); + + if (err == noErr) + err = GetControlByID(window, &statusID, &status); + + if (err == noErr) + err = GetControlByID(window, &progressID, &progress); + + if (err == noErr) + { + ShowWindow(window); + err = ActivateWindow(window, TRUE); + } /* if */ + + if (err == noErr) + { + err = SetControlData(progress, kControlEntireControl, + kControlProgressBarAnimatingTag, + sizeof (b), &b); + } /* if */ + + if (err == noErr); + UI_SET_FUNC_POINTERS(carbon); + + return(err == noErr); +} /* ui_init_carbon */ + +#endif /* end of ui_carbon.c ... */ diff --git a/ui_stdio.c b/ui_stdio.c index c268568..cdb6872 100644 --- a/ui_stdio.c +++ b/ui_stdio.c @@ -1,53 +1,50 @@ #include +#include +#include #include #include "platform.h" #include "ui.h" -int ui_init(void) +static void ui_title_stdio(const char *str) { - return(1); /* always succeeds. */ -} /* ui_init */ - - -void ui_title(const char *str) -{ -} /* ui_title */ + printf("=== %s ===\n\n", str); +} /* ui_title_stdio */ -void ui_deinit(void) +static void ui_real_deinit_stdio(void) { printf("\n\nHit enter to quit.\n\n"); getchar(); -} /* ui_deinit */ +} /* ui_deinit_stdio */ -void ui_pump(void) +static void ui_pump_stdio(void) { /* no-op. */ -} /* ui_pump */ +} /* ui_pump_stdio */ -void ui_add_to_log(const char *str, int debugging) +static void ui_add_to_log_stdio(const char *str, int debugging) { printf("%s%s\n", debugging ? "debug: " : "", str); -} /* ui_add_to_log */ +} /* ui_add_to_log_stdio */ -void ui_fatal(const char *str) +static void ui_fatal_stdio(const char *str) { fprintf(stderr, "\n%s\n\n", str); -} /* ui_fatal */ +} /* ui_fatal_stdio */ -void ui_success(const char *str) +static void ui_success_stdio(const char *str) { fprintf(stderr, "\n%s\n\n", str); -} /* ui_success */ +} /* ui_success_stdio */ -void ui_total_progress(int percent) +static void ui_total_progress_stdio(int percent) { static int lastpercent = -1; if (percent != lastpercent) @@ -57,16 +54,16 @@ void ui_total_progress(int percent) if (percent == 100) printf("\n"); } /* if */ -} /* ui_total_progress */ +} /* ui_total_progress_stdio */ -void ui_status(const char *str) +static void ui_status_stdio(const char *str) { printf("Current operation: %s\n", str); -} /* ui_status */ +} /* ui_status_stdio */ -int ui_prompt_yn(const char *question) +static int ui_prompt_yn_stdio(const char *question) { int c; while (1) @@ -81,10 +78,10 @@ int ui_prompt_yn(const char *question) } /* while */ return(1); -} /* ui_prompt_yn */ +} /* ui_prompt_yn_stdio */ -int ui_prompt_ny(const char *question) +static int ui_prompt_ny_stdio(const char *question) { int c; while (1) @@ -99,7 +96,68 @@ int ui_prompt_ny(const char *question) } /* while */ return(0); -} /* ui_prompt_ny */ +} /* ui_prompt_ny_stdio */ + + +static void ui_msgbox_stdio(const char *str) +{ + printf("\n\n-----\n%s\n-----\n\nHit enter to continue.\n\n", str); + getchar(); +} /* ui_msgbox_stdio */ + + +static int ui_file_picker_stdio(char *buf, size_t bufsize) +{ + while (1) + { + size_t len; + puts("Please enter the path, or blank to cancel.\n> "); + + buf[0] = '\0'; + fgets(buf, bufsize, stdin); + len = strlen(buf); + while ((len > 0) && ((buf[len-1] == '\n') || (buf[len-1] == '\r'))) + buf[--len] = '\0'; + + if (len == 0) + return(0); /* user "cancelled". */ + + if (file_exists(buf)) + return(1); /* user entered valid path. */ + + puts("That path does not exist. Please try again.\n\n"); + } /* while */ + + return(0); /* should never hit this. */ +} /* ui_file_picker_stdio */ + + +static int ui_show_readme_stdio(const char *fname,const char *text) +{ + /* + * Cheat for now and push this off to "less", which will warn if the + * readme is a binary file and prompt the user about avoiding it. + */ + + size_t allocsize = strlen(fname) + 32; + char *cmd = (char *) alloca(allocsize); + if (!cmd) + { + _fatal("Out of memory."); + return(0); + } /* if */ + + snprintf(cmd, allocsize, "less %s", fname); + system(cmd); /* !!! FIXME: error check? */ + return(1); +} /* ui_show_readme_stdio */ + + +int ui_init_stdio(void) +{ + UI_SET_FUNC_POINTERS(stdio); + return(1); /* always succeeds. */ +} /* ui_init */ -/* end of ui_stdio.h ... */ +/* end of ui_stdio.c ... */