diff options
author | Adrian Robert <Adrian.B.Robert@gmail.com> | 2008-07-15 18:15:18 +0000 |
---|---|---|
committer | Adrian Robert <Adrian.B.Robert@gmail.com> | 2008-07-15 18:15:18 +0000 |
commit | edfda78355c5528eee489fa8a7f9c73bf8e734f2 (patch) | |
tree | 78d2414d9791e1efc17ec9b35b438ae35602340a /src | |
parent | 1391cd548782097e34d7856ec4f20ca90bdf2c26 (diff) | |
download | emacs-edfda78355c5528eee489fa8a7f9c73bf8e734f2.tar.gz |
merging Emacs.app (NeXTstep port)
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 131 | ||||
-rw-r--r-- | src/Makefile.in | 99 | ||||
-rw-r--r-- | src/blockinput.h | 12 | ||||
-rw-r--r-- | src/callproc.c | 5 | ||||
-rw-r--r-- | src/config.in | 52 | ||||
-rw-r--r-- | src/dispextern.h | 15 | ||||
-rw-r--r-- | src/dispnew.c | 18 | ||||
-rw-r--r-- | src/emacs.c | 84 | ||||
-rw-r--r-- | src/font.c | 16 | ||||
-rw-r--r-- | src/font.h | 3 | ||||
-rw-r--r-- | src/fontset.c | 8 | ||||
-rw-r--r-- | src/frame.c | 41 | ||||
-rw-r--r-- | src/frame.h | 18 | ||||
-rw-r--r-- | src/fringe.c | 2 | ||||
-rw-r--r-- | src/getloadavg.c | 7 | ||||
-rw-r--r-- | src/image.c | 256 | ||||
-rw-r--r-- | src/keyboard.c | 55 | ||||
-rw-r--r-- | src/keyboard.h | 53 | ||||
-rw-r--r-- | src/keymap.c | 68 | ||||
-rw-r--r-- | src/lisp.h | 7 | ||||
-rw-r--r-- | src/lread.c | 4 | ||||
-rw-r--r-- | src/menu.c | 74 | ||||
-rw-r--r-- | src/nsfns.m | 2668 | ||||
-rw-r--r-- | src/nsgui.h | 208 | ||||
-rw-r--r-- | src/nsimage.m | 480 | ||||
-rw-r--r-- | src/nsmenu.m | 1948 | ||||
-rw-r--r-- | src/nsselect.m | 624 | ||||
-rw-r--r-- | src/nsterm.h | 827 | ||||
-rw-r--r-- | src/nsterm.m | 6598 | ||||
-rw-r--r-- | src/process.c | 8 | ||||
-rw-r--r-- | src/s/darwin.h | 35 | ||||
-rw-r--r-- | src/syntax.c | 13 | ||||
-rw-r--r-- | src/sysselect.h | 4 | ||||
-rw-r--r-- | src/termhooks.h | 3 | ||||
-rw-r--r-- | src/terminal.c | 2 | ||||
-rw-r--r-- | src/terminfo.c | 3 | ||||
-rw-r--r-- | src/w32gui.h | 93 | ||||
-rw-r--r-- | src/window.c | 3 | ||||
-rw-r--r-- | src/xdisp.c | 68 | ||||
-rw-r--r-- | src/xfaces.c | 56 |
40 files changed, 14476 insertions, 193 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 848046ece79..e8a514d2a50 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,134 @@ +2008-07-15 Adrian Robert <Adrian.B.Robert@gmail.com> + + Changes and additions for NeXTstep windowing system (Cocoa and + GNUstep) support. + + * Makefile.in + * config.in: Support defines and build commands for NS port. + * blockinput.h (BLOCK_INPUT, UNBLOCK_INPUT, TOTALLY_UNBLOCK_INPUT) + (UNBLOCK_INPUT_TO): Don't use under NS unless EXPERIMENTAL_CONTROL_G. + * callproc.c (set_initial_environment): Initialize + Vprocess_environment under CANNOT_DUMP (fixes crash when + batch-compiling for bootstrap, due to Chris Hall). + * dispextern.h: Include nsgui.h and add needed typedefs under NS + windowing. + (struct face): Add synth_ital field. + * dispnew.c: Include nsterm.h when compiling under NS windowing. + (init_display): Initialize Vinitial_window_system to "ns" when so + compiled. + * emacs.c: Include GSConfig.h when compiling under GNUstep. + (display_arg): Use under NS. + (main): Under NS, allocate autorelease pool and handle command line + args. Move syms_of_xmenu() call under #ifdef HAVE_X_WINDOWS. + (standard_args): Add NS-specific args. + (shut_down_emacs): Shut down NS terminal if compiled under NS. + * font.c (DEFAULT_ENCODING): New variable. + (font_find_for_lface): Use it. + (syms_of_font): Load syms_of_nsfont under NS. + * font.h: Declare nsfont_driver when compiled under NS. + * fontset.c: When compiling under NS, include nsterm.h. + (fontset_from_font): Autoconstruct fontset under NS. + * frame.c (various): Under NS, include nsterm.h, add Qns window system + symbol, document and use it. + (make_initial_frame): Call init_frame_faces(f) in CANNOT_DUMP case -- + patch to fix crash due to different init order, due to Chris Hall and + Yamamoto Mitsuharu. + (do_switch_frame): When for_deletion under Cocoa, add + Fraise_frame(Qnil). + (x_set_frame_parameters): Ensure font attribute changes are picked up. + (x_get_arg): Allow "yes" and "no" as boolean values. + (syms_of_frame): Declare Qns. Init Vdefault_frame_scroll_bars to + Qright under Cocoa. + (focus-follows-mouse): Default to 0 under NS. + * frame.h (enum output_method): Add output_ns. + (external_tool_bar, external_menu_bar, FRAME_EXTERNAL_TOOLBAR) + (FRAME_EXTERNAL_MENU_BAR): Use under NS. + (FRAME_WINDOW_P): NS-specific definition. + * fringe.c (max_used_fringe_bitmap): Make public. + * getloadavg.c (mach/mach.h): Include it under NeXT descendant OS's. + (getloadavg): Use NeXT code under descendant OS's. + * image.c (includes and header section, x_create_bitmap_from_data) + (x_create_bitmap_from_file, free_bitmap_record, image_background) + (image_background_transparent, x_clear_image_1) + (x_create_x_image_and_pixmap, x_destroy_x_image, x_put_x_image) + (Create_Pixmap_From_Bitmap_Data, xpm_load_image, lookup_rgb_color) + (x_to_xcolors, x_from_xcolors, x_disable_image) + (x_build_heuristic_mask, syms_of_image): Add NS support parallel to + other GUIs, including XPM support using code originally written for + Carbon GUI. + (png_load, jpeg_load, tiff_load, gif_load): Added implementations + using NS API. + (image_ascent): Use font metrics macros instead of direct struct field + access. + * keyboard.c (includes): Add nsterm.h when compiling under NS. + (kbd_buffer_get_event): Handle NS as other GUI windowing systems. + Also, handle NS as GTK for menu bar purposes. + (make_lispy_event): Handle NS as other GUI windowing systems, and as X + toolkit where they differ. + (parse_menu_item): Prefer keybindings using 'super' modifier. Also, + use cachelist, still needed under NS. + * keyboard.h (ENCODE_MENU_STRING, XtPointer, Boolean): Handle as + NTGUI. + (struct widget_value): Define it here for menu.c. + * keymap.c (includes): Include modifier internals. + (lisp_to_mod, modifier_sequence_p): New functions, compiled only under + NS. + (where_is_internal, Fwhere_is_internal): When compiled under NS, add + support for preferring sequences using certain modifiers, specified by + the FIRSTONLY argument. + * lisp.h (hash_remove): Rename to avoid name clash when compiling + under NS GNUstep implementation. + (USE_LSB_TAG): Use it under Cocoa when compiling under NS. + * lread.c (init_lread): Treat NS as HAVE_CARBON for turn_off_warning. + * menu.c: Include nsterm.h under NS. + (single_menu_item, parse_single_submenu, xmalloc_widget_value) + (free_menubar_widget_tree_value, update_submenu_strings) + (find_and_call_menu_selection): Treat NS as X and NT. + (find_and_return_menu_selection): New function, used for popup menus. + * nsgui.h + * nsterm.h + * nsfns.m + * nsimage.m + * nsmenu.m + * nsselect.m + * nsterm.m: New files. + * process.c (wait_reading_process_output): Under NS, call ns_select() + instead of plain select(). + * syntax.c (char_quoted): Under NS, avoid a crash when called near + beginning of buffer. + * sysselect.h (init_process): Rename when compiling under Cocoa to + avoid name conflict. + * termhooks.h (display_info): Add ns_display_info to union. + * terminal.c (Fterminal_live_p): Add ns to terminal types. + * terminfo.c (UP, BC, PC): Don't declare when compiling under NS in + COCOA environment. + * unexnext.c: Update to work with mach API on Mac OS X, and to use new + unexec() signature. (Note, this will dump, but the resulting file + crashes; unexosx is used instead; keeping around for reference and + possible aid in getting dump working under GNUstep.) + * w32gui.h (button_type, widget_value): Remove definitions (now in + keyboard.h). + * window.c: Include nsterm.h when compiling under NS. + * xdisp.c (includes): Include nsterm.h when compiling under NS. + (set_frame_menubar, update_menu_bar, display_menu_bar): Handle NS as + other GUI windowing systems. + (update_tool_bar, redisplay_tool_bar, redisplay_window): Handle NS as + GTK. + (x_consider_frame_title): Under NS, set icon type and frame + modified-state indicator; use ns_set_name_as_filename() when using + formatted title. + (update_window_cursor): Make public when compiling under NS. + (display_hourglass_p, syms_of_xdisp, hourglass_shown_p) + (hourglass_atimer, Vhourglass_delay + * xfaces.c (header section, init_frame_faces, clear_font_table) + (defined_color, unload_color, x_face_list_fonts) + (prepare_face_for_display): Add NS support parallel to other GUIs) + (emulate GCs like other non-X GUIs. + (split_font_name): Don't lowercase font name under NS. + (merge_face_ref, Finternal_set_lisp_face_attribute): Support stippling + under NS. + * s/darwin.h: Add support for compilation under NS. + 2008-07-15 Jason Rumney <jasonr@gnu.org> * w32fns.c (Fx_create_frame): Remove duplicate unwind_protect. diff --git a/src/Makefile.in b/src/Makefile.in index 9fc66979f25..123a49fca68 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -112,6 +112,11 @@ SHELL=/bin/sh #endif #endif +/* Under GNUstep, putting libc on the link line causes problems. */ +#ifdef GNUSTEP +#define LIB_STANDARD +#endif + /* Unless inhibited or changed, use -lg to link for debugging. */ #ifndef LIBS_DEBUG #define LIBS_DEBUG -lg @@ -229,6 +234,15 @@ STARTFILES = START_FILES #endif /* not ORDINARY_LINK */ +#ifdef GNUSTEP +/* Pull in stuff from GNUstep-make. */ +FOUNDATION_LIB=gnu +GUI_LIB=gnu +include $(GNUSTEP_MAKEFILES)/Additional/base.make +include $(GNUSTEP_MAKEFILES)/Additional/gui.make +shared=no +#endif + #ifdef HAVE_DBUS DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ @@ -249,8 +263,11 @@ DBUS_OBJ = dbusbind.o /* C_SWITCH_X_SITE must come before C_SWITCH_X_MACHINE and C_SWITCH_X_SYSTEM since it may have -I options that should override those two. */ ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(MYCPPFLAGS) -I. -I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_SITE C_SWITCH_X_SITE C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS_SOUND} ${RSVG_CFLAGS} ${DBUS_CFLAGS} ${CFLAGS} @FREETYPE_CFLAGS@ @FONTCONFIG_CFLAGS@ @LIBOTF_CFLAGS@ @M17N_FLT_CFLAGS@ +.SUFFIXES: .m .c.o: $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $< +.m.o: + $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) GNU_OBJC_CFLAGS $< #ifndef LIBX11_SYSTEM #define LIBX11_SYSTEM @@ -260,13 +277,8 @@ ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(MYCPPFLAGS) -I. -I${srcdir} C_SWITCH_MACHIN #define LIB_X11_LIB -lX11 #endif -/* xmenu.c should not be compiled on OSX. */ -#ifndef HAVE_CARBON -XMENU_OBJ = xmenu.o -#endif - #ifdef HAVE_X_WINDOWS - +XMENU_OBJ = xmenu.o XOBJ= xterm.o xfns.o xselect.o xrdb.o fontset.o xsmfns.o fringe.o image.o #ifdef HAVE_MENUS @@ -518,6 +530,21 @@ emacsapp = $(PWD)/$(mac)Emacs.app/ emacsappsrc = ${srcdir}/../mac/Emacs.app/ #endif +#ifdef HAVE_NS +/* Object files for NeXTstep */ +NS_OBJ= nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o \ + fontset.o fringe.o image.o +emacsapp = $(PWD)/../nextstep/build/Emacs.app/ +FONT_DRIVERS = nsfont.o +#ifdef GNUSTEP +emacsappsrc = ${srcdir}/../nextstep/GNUstep/Emacs.base +emacsbindir = $(emacsapp) +#else +emacsappsrc = ${srcdir}/../nextstep/Cocoa/Emacs.base +emacsbindir = $(emacsapp)/Contents/MacOS/ +#endif /* GNUSTEP */ +#endif /* HAVE_NS */ + #ifdef HAVE_WINDOW_SYSTEM #ifdef HAVE_X_WINDOWS #if defined (HAVE_XFT) @@ -545,7 +572,7 @@ obj= dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ process.o callproc.o \ region-cache.o sound.o atimer.o \ doprnt.o strftime.o intervals.o textprop.o composite.o md5.o \ - $(MSDOS_OBJ) $(MAC_OBJ) $(CYGWIN_OBJ) $(FONT_DRIVERS) + $(MSDOS_OBJ) $(MAC_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_DRIVERS) /* Object files used on some machine or other. These go in the DOC file on all machines @@ -553,6 +580,7 @@ obj= dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \ xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \ mac.o macterm.o macfns.o macmenu.o macselect.o fontset.o \ + nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o \ w32.o w32console.o w32fns.o w32heap.o w32inevt.o \ w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o $(FONT_DRIVERS) @@ -876,6 +904,7 @@ SOME_MACHINE_LISP = ../lisp/mouse.elc \ Note that SunOS needs -lm to come before -lc; otherwise, you get duplicated symbols. If the standard libraries were compiled with GCC, we might need gnulib again after them. */ + LIBES = $(LOADLIBES) $(LIBS) $(LIBX) $(LIBSOUND) $(RSVG_LIBS) $(DBUS_LIBS) \ LIBGPM LIBRESOLV LIBS_SYSTEM LIBS_MACHINE LIBS_TERMCAP \ LIBS_DEBUG $(GETLOADAVG_LIBS) \ @@ -910,7 +939,10 @@ emacs${EXEEXT}: temacs${EXEEXT} ${etc}DOC ${lisp} ${SOME_MACHINE_LISP} @: bootstrap-emacs, so let us replace it. -ln -f emacs${EXEEXT} bootstrap-emacs${EXEEXT} #endif /* ! defined (CANNOT_DUMP) */ +/* XXX: not working under NS currently due to path shenanigans.. */ +#ifndef HAVE_NS -./emacs -q -batch -f list-load-path-shadows +#endif /* We run make-docfile twice because the command line may get too long on some systems. */ @@ -935,9 +967,13 @@ ${libsrc}make-docfile${EXEEXT}: temacs${EXEEXT}: $(LOCALCPP) $(STARTFILES) stamp-oldxmenu ${obj} ${otherobj} OBJECTS_MACHINE prefix-args${EXEEXT} echo "${obj} ${otherobj} " OBJECTS_MACHINE > buildobj.lst +#ifdef GNUSTEP + $(CC) -rdynamic YMF_PASS_LDFLAGS (${TEMACS_LDFLAGS}) -o temacs ${obj} ${otherobj} OBJECTS_MACHINE ${LIBES} +#else $(LD) YMF_PASS_LDFLAGS (${STARTFLAGS} ${TEMACS_LDFLAGS}) $(LDFLAGS) \ -o temacs ${STARTFILES} ${obj} ${otherobj} \ OBJECTS_MACHINE ${LIBES} +#endif /* We do not use ALL_LDFLAGS because LD_SWITCH_SYSTEM and LD_SWITCH_MACHINE often contain options that have to do with using Emacs''s crt0, @@ -1080,7 +1116,7 @@ fontset.o: dispextern.h fontset.h fontset.c ccl.h buffer.h character.h \ getloadavg.o: getloadavg.c $(config_h) image.o: image.c frame.h window.h dispextern.h blockinput.h atimer.h \ systime.h xterm.h w32term.h w32gui.h macterm.h macgui.h font.h \ - $(config_h) + nsterm.h nsgui.h $(config_h) indent.o: indent.c frame.h window.h indent.h buffer.h $(config_h) termchar.h \ termopts.h disptab.h region-cache.h character.h category.h composite.h \ dispextern.h keyboard.h @@ -1089,7 +1125,8 @@ insdel.o: insdel.c window.h buffer.h $(INTERVAL_SRC) blockinput.h character.h \ keyboard.o: keyboard.c termchar.h termhooks.h termopts.h buffer.h character.h \ commands.h frame.h window.h macros.h disptab.h keyboard.h syssignal.h \ systime.h dispextern.h syntax.h $(INTERVAL_SRC) blockinput.h \ - atimer.h xterm.h puresize.h msdos.h keymap.h w32term.h macterm.h $(config_h) + atimer.h xterm.h puresize.h msdos.h keymap.h w32term.h macterm.h nsterm.h \ + $(config_h) keymap.o: keymap.c buffer.h commands.h keyboard.h termhooks.h blockinput.h \ atimer.h systime.h puresize.h character.h intervals.h keymap.h window.h \ $(config_h) @@ -1155,15 +1192,15 @@ widget.o: widget.c xterm.h frame.h dispextern.h widgetprv.h \ window.o: window.c indent.h commands.h frame.h window.h buffer.h termchar.h \ disptab.h keyboard.h dispextern.h msdos.h composite.h \ keymap.h blockinput.h atimer.h systime.h $(INTERVAL_SRC) \ - xterm.h w32term.h macterm.h $(config_h) + xterm.h w32term.h macterm.h nsterm.h $(config_h) xdisp.o: xdisp.c macros.h commands.h process.h indent.h buffer.h dispextern.h \ coding.h termchar.h frame.h window.h disptab.h termhooks.h character.h \ charset.h $(config_h) keyboard.h $(INTERVAL_SRC) region-cache.h xterm.h \ - w32term.h macterm.h msdos.h composite.h fontset.h blockinput.h atimer.h \ - systime.h keymap.h font.h + w32term.h macterm.h nsterm.h msdos.h composite.h fontset.h blockinput.h \ + atimer.h systime.h keymap.h font.h xfaces.o: xfaces.c dispextern.h frame.h xterm.h buffer.h blockinput.h \ window.h character.h charset.h msdos.h dosfns.h composite.h atimer.h \ - systime.h keyboard.h fontset.h w32term.h macterm.h $(INTERVAL_SRC) \ + systime.h keyboard.h fontset.h w32term.h macterm.h nsterm.h $(INTERVAL_SRC) \ termchar.h termhooks.h font.h $(config_h) xfns.o: xfns.c buffer.h frame.h window.h keyboard.h xterm.h dispextern.h \ $(srcdir)/../lwlib/lwlib.h blockinput.h atimer.h systime.h epaths.h \ @@ -1276,7 +1313,38 @@ macosx-app: macosx-bundle ${emacsapp}Contents/MacOS/Emacs ${emacsapp}Contents/MacOS/Emacs: emacs${EXEEXT} mkdir -p ${emacsapp}Contents/MacOS/; cd ${emacsapp}Contents/MacOS/; cp ../../../../src/emacs${EXEEXT} Emacs${EXEEXT} -#endif +#endif /* HAVE_CARBON */ + +#ifdef HAVE_NS +abbrev.o buffer.o callint.o cmds.o dispnew.o editfns.o fileio.o frame.o \ + fontset.o indent.o insdel.o keyboard.o macros.o minibuf.o msdos.o process.o \ + scroll.o sysdep.o term.o widget.o window.o xdisp.o xfaces.o xfns.o \ + xterm.o xselect.o sound.o: nsgui.h +nsfns.o: nsfns.m charset.h nsterm.h nsgui.h frame.h window.h buffer.h \ + dispextern.h nsgui.h fontset.h $(INTERVAL_SRC) keyboard.h blockinput.h \ + atimer.h systime.h epaths.h termhooks.h coding.h systime.h $(config_h) +nsmenu.o: nsmenu.m termhooks.h frame.h window.h dispextern.h \ + nsgui.h keyboard.h blockinput.h atimer.h systime.h buffer.h \ + nsterm.h $(config_h) +nsterm.o: nsterm.m blockinput.h atimer.h systime.h syssignal.h nsterm.h \ + nsgui.h frame.h charset.h ccl.h dispextern.h fontset.h termhooks.h \ + termopts.h termchar.h disptab.h buffer.h window.h keyboard.h \ + $(INTERVAL_SRC) process.h coding.h $(config_h) +nsselect.o: nsselect.m blockinput.h nsterm.h nsgui.h frame.h $(config_h) +nsimage.o: nsimage.m nsterm.h +nsfont.o: nsterm.h dispextern.h frame.h lisp.h $(config_h) + +${emacsapp}: ${emacsappsrc} + mkdir -p ${emacsapp} + ( cd ${emacsappsrc} ; tar cfh - . ) | ( cd ${emacsapp} ; tar xf - ) + +${emacsbindir}Emacs: emacs${EXEEXT} + mkdir -p ${emacsbindir} + cp -f emacs${EXEEXT} ${emacsbindir}Emacs + +ns-app: ${emacsapp} ${emacsbindir}Emacs + +#endif /* HAVE_NS */ mostlyclean: rm -f temacs${EXEEXT} prefix-args${EXEEXT} core *.core \#* *.o libXMenu11.a liblw.a @@ -1285,6 +1353,9 @@ mostlyclean: rm -f buildobj.lst clean: mostlyclean rm -f emacs-*.*.*${EXEEXT} emacs${EXEEXT} +#ifdef GNUSTEP + rm -f *.d +#endif /* bootstrap-clean is used to clean up just before a bootstrap. It should remove all files generated during a compilation/bootstrap, but not things like config.status or TAGS. */ diff --git a/src/blockinput.h b/src/blockinput.h index bcf8d17e870..fb338d9df49 100644 --- a/src/blockinput.h +++ b/src/blockinput.h @@ -59,6 +59,16 @@ extern int interrupt_input_pending; extern int pending_atimers; +#if defined HAVE_NS && !defined COCOA_EXPERIMENTAL_CTRL_G +/* NS does not use interrupt-driven input processing (yet), so this is + unneeded and moreover was causing problems. */ +#define BLOCK_INPUT +#define UNBLOCK_INPUT +#define TOTALLY_UNBLOCK_INPUT +#define UNBLOCK_INPUT_TO(LEVEL) + +#else + /* Begin critical section. */ #define BLOCK_INPUT (interrupt_input_blocked++) @@ -115,6 +125,8 @@ extern int pending_atimers; } \ while (0) +#endif /* defined HAVE_NS && !defined COCOA_EXPERIMENTAL_CTRL_G */ + #define UNBLOCK_INPUT_RESIGNAL UNBLOCK_INPUT /* In critical section ? */ diff --git a/src/callproc.c b/src/callproc.c index 746c78243c4..a6de7668c15 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -1587,8 +1587,11 @@ set_initial_environment () register char **envp; #ifndef CANNOT_DUMP if (initialized) -#endif { +#else + { + Vprocess_environment = Qnil; +#endif for (envp = environ; *envp; envp++) Vprocess_environment = Fcons (build_string (*envp), Vprocess_environment); diff --git a/src/config.in b/src/config.in index ae2a24dc46a..714d1d44f80 100644 --- a/src/config.in +++ b/src/config.in @@ -30,6 +30,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ /* Define to 1 if the mktime function is broken. */ #undef BROKEN_MKTIME +/* Define to 1 if you are trying experimental enhanced Ctrl-g support using NS + windowing under MacOS X. */ +#undef COCOA_EXPERIMENTAL_CTRL_G + /* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP systems. This function is required for `alloca.c' support on those systems. */ @@ -472,6 +476,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ /* Define to 1 if you have the <nlist.h> header file. */ #undef HAVE_NLIST_H +/* Define to 1 if you are using the NeXTstep API, either GNUstep or Cocoa on + Mac OS X. */ +#undef HAVE_NS + /* Define to 1 if personality LINUX32 can be set. */ #undef HAVE_PERSONALITY_LINUX32 @@ -798,6 +806,12 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ /* Define to 1 if you don't have struct exception in math.h. */ #undef NO_MATHERR +/* Define to 1 if you are using NS windowing under MacOS X. */ +#undef NS_IMPL_COCOA + +/* Define to 1 if you are using NS windowing under GNUstep. */ +#undef NS_IMPL_GNUSTEP + /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT @@ -965,8 +979,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #undef volatile -/* If we're using any sort of window system, define some consequences. */ -#ifdef HAVE_X_WINDOWS +/* If we're using X11/Carbon/GNUstep, define some consequences. */ +#if defined HAVE_X_WINDOWS || defined(HAVE_CARBON) || defined(HAVE_NS) #define HAVE_WINDOW_SYSTEM #define MULTI_KBOARD #define HAVE_MOUSE @@ -978,13 +992,15 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #define MULTI_KBOARD #endif -/* If we're using the Carbon API on Mac OS X, define a few more - variables as well. */ -#ifdef HAVE_CARBON -#define HAVE_WINDOW_SYSTEM -#define HAVE_MOUSE +/* Sadly for now, GNUstep dump does not work. */ +#ifdef NS_IMPL_GNUSTEP +#define CANNOT_DUMP #endif +/* PENDING: These are used for the Carbon port only. */ +#undef MAC_OS +#undef MAC_OSX + /* Define USER_FULL_NAME to return a string that is the user's full name. It can assume that the variable `pw' @@ -1037,6 +1053,28 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include config_opsysfile #include config_machfile +/* Set up some defines, C and LD flags for NeXTstep interface on GNUstep. + (There is probably a better place to do this, but right now the Cocoa + side does this in s/darwin.h, following the Carbon port, and we cannot + parallel this exactly since GNUstep is multi-OS. */ +#ifdef HAVE_NS +# ifdef C_SWITCH_SYSTEM +# undef C_SWITCH_SYSTEM +# endif +# ifdef NS_IMPL_GNUSTEP +/* See also .m.o rule in Makefile.in */ +# define C_SWITCH_X_SYSTEM -MMD -MP -D_REENTRANT -fPIC -fno-strict-aliasing +# define LD_SWITCH_SITE -lgnustep-gui -lgnustep-base -lobjc $(CONFIG_SYSTEM_LIBS) -lpthread +# define GNU_OBJC_CFLAGS -fgnu-runtime -Wno-import -fconstant-string-class=NSConstantString -DGNUSTEP_BASE_LIBRARY=1 -DGNU_GUI_LIBRARY=1 -DGNU_RUNTIME=1 -DGSWARN -DGSDIAGNOSE +# define OTHER_FILES ns-app +# else /* COCOA */ +# define C_SWITCH_X_SYSTEM +# define GNU_OBJC_CFLAGS +# endif /* COCOA */ +#endif /* HAVE_NS */ + + + /* If no remapping takes place, static variables cannot be dumped as pure, so don't worry about the `static' keyword. */ #ifdef NO_REMAP diff --git a/src/dispextern.h b/src/dispextern.h index e6064476d0c..180820d4b80 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -69,6 +69,14 @@ typedef Pixmap XImagePtr; typedef XImagePtr XImagePtr_or_DC; #endif +#ifdef HAVE_NS +#include "nsgui.h" +/* following typedef needed to accomodate the MSDOS port, believe it or not */ +typedef struct ns_display_info Display_Info; +typedef Pixmap XImagePtr; +typedef XImagePtr XImagePtr_or_DC; +#endif + #ifndef NativeRectangle #define NativeRectangle int #endif @@ -1563,6 +1571,13 @@ struct face /* If non-zero, use overstrike (to simulate bold-face). */ unsigned overstrike : 1; +/* NOTE: this is not used yet, but eventually this impl should be done + similarly to overstrike */ +#ifdef HAVE_NS + /* If non-zero, use geometric rotation (to simulate italic). */ + unsigned synth_ital : 1; +#endif + /* Next and previous face in hash collision list of face cache. */ struct face *next, *prev; diff --git a/src/dispnew.c b/src/dispnew.c index f182af77d3b..d95ce7c39e7 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -63,6 +63,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "macterm.h" #endif /* MAC_OS */ +#ifdef HAVE_NS +#include "nsterm.h" +#endif + /* Include systime.h after xterm.h to avoid double inclusion of time.h. */ #include "systime.h" @@ -6895,6 +6899,20 @@ init_display () } #endif /* MAC_OS */ +#ifdef HAVE_NS + if (!inhibit_window_system +#ifndef CANNOT_DUMP + && initialized +#endif + ) + { + Vinitial_window_system = intern("ns"); + Vwindow_system_version = make_number(10); + adjust_frame_glyphs_initially (); + return; + } +#endif + /* If no window system has been specified, try to use the terminal. */ if (! isatty (0)) { diff --git a/src/emacs.c b/src/emacs.c index 8dfdf9062a1..a6e59f8c9fe 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -46,6 +46,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "w32heap.h" /* for prototype of sbrk */ #endif +#ifdef NS_IMPL_GNUSTEP +/* At least under Debian, GSConfig is in a subdirectory. --Stef */ +#include <GNUstepBase/GSConfig.h> +#endif + #include "lisp.h" #include "commands.h" #include "intervals.h" @@ -193,7 +198,7 @@ int running_asynch_code; extern int inherited_pgroup; #endif -#ifdef HAVE_X_WINDOWS +#if defined(HAVE_X_WINDOWS) || defined(HAVE_NS) /* If non-zero, -d was specified, meaning we're using some window system. */ int display_arg; #endif @@ -838,7 +843,7 @@ main (argc, argv run_time_remap (argv[0]); #endif -#ifdef MAC_OSX +#if defined (MAC_OSX) || defined (NS_IMPL_COCOA) if (!initialized) unexec_init_emacs_zone (); #endif @@ -1410,6 +1415,38 @@ main (argc, argv no_loadup = argmatch (argv, argc, "-nl", "--no-loadup", 6, NULL, &skip_args); +#ifdef HAVE_NS + ns_alloc_autorelease_pool(); + if (!noninteractive) + { + char *tmp; + display_arg = 4; +#ifdef NS_IMPL_COCOA + if (skip_args < argc) + { + if (!strncmp(argv[skip_args], "-psn", 4)) + { + skip_args += 1; + } + else + { + if (skip_args+1 < argc && !strncmp(argv[skip_args+1], "-psn", 4)) + skip_args += 2; + } + } +#endif + /* This used for remote operation.. not fully implemented yet. */ + if (argmatch (argv, argc, "-_NSMachLaunch", 0, 3, &tmp, &skip_args)) + display_arg = 4; + else if (argmatch (argv, argc, "-MachLaunch", 0, 3, &tmp, &skip_args)) + display_arg = 4; + else if (argmatch (argv, argc, "-macosx", 0, 2, NULL, &skip_args)) + display_arg = 4; + else if (argmatch (argv, argc, "-NSHost", 0, 3, &tmp, &skip_args)) + display_arg = 4; + } +#endif /* HAVE_NS */ + #ifdef HAVE_X_WINDOWS /* Stupid kludge to catch command-line display spec. We can't handle this argument entirely in window system dependent code @@ -1487,6 +1524,13 @@ main (argc, argv init_mac_osx_environment (); #endif +#ifdef HAVE_NS +#ifndef CANNOT_DUMP + if (initialized) +#endif + ns_init_paths (); +#endif + /* egetenv is a pretty low-level facility, which may get called in many circumstances; it seems flimsy to put off initializing it until calling init_callproc. */ @@ -1588,6 +1632,7 @@ main (argc, argv #ifdef HAVE_X_WINDOWS syms_of_xterm (); syms_of_xfns (); + syms_of_xmenu (); syms_of_fontset (); #ifdef HAVE_X_SM syms_of_xsmfns (); @@ -1599,13 +1644,6 @@ main (argc, argv syms_of_menu (); -#ifndef HAVE_NTGUI -#ifndef MAC_OS - /* Called before init_window_once for Mac OS Classic. */ - syms_of_xmenu (); -#endif -#endif - #ifdef HAVE_NTGUI syms_of_w32term (); syms_of_w32fns (); @@ -1622,6 +1660,14 @@ main (argc, argv syms_of_fontset (); #endif /* MAC_OSX && HAVE_CARBON */ +#ifdef HAVE_NS + syms_of_nsterm (); + syms_of_nsfns (); + syms_of_nsmenu (); + syms_of_nsselect (); + syms_of_fontset (); +#endif /* HAVE_NS */ + #ifdef HAVE_DBUS syms_of_dbusbind (); #endif /* HAVE_DBUS */ @@ -1843,6 +1889,15 @@ struct standard_args standard_args[] = { "-color", "--color", 5, 0}, { "-no-splash", "--no-splash", 3, 0 }, { "-no-desktop", "--no-desktop", 3, 0 }, +#ifdef HAVE_NS + { "-NSAutoLaunch", 0, 5, 1 }, + { "-NXAutoLaunch", 0, 5, 1 }, + { "-disable-font-backend", "--disable-font-backend", 65, 0 }, + { "-_NSMachLaunch", 0, 85, 1 }, + { "-MachLaunch", 0, 85, 1 }, + { "-macosx", 0, 85, 0 }, + { "-NSHost", 0, 85, 1 }, +#endif /* These have the same priority as ordinary file name args, so they are not reordered with respect to those. */ { "-L", "--directory", 0, 1 }, @@ -1862,6 +1917,13 @@ struct standard_args standard_args[] = { "-visit", "--visit", 0, 1 }, { "-file", "--file", 0, 1 }, { "-insert", "--insert", 0, 1 }, +#ifdef HAVE_NS + { "-NXOpen", 0, 0, 1 }, + { "-NXOpenTemp", 0, 0, 1 }, + { "-NSOpen", 0, 0, 1 }, + { "-NSOpenTemp", 0, 0, 1 }, + { "-GSFilePath", 0, 0, 1 }, +#endif /* This should be processed after ordinary file name args and the like. */ { "-kill", "--kill", -10, 0 }, }; @@ -2158,6 +2220,10 @@ shut_down_emacs (sig, no_x, stuff) #ifdef MSDOS dos_cleanup (); #endif + +#ifdef HAVE_NS + ns_term_shutdown (sig); +#endif } diff --git a/src/font.c b/src/font.c index 91bb84239ae..26adda26e47 100644 --- a/src/font.c +++ b/src/font.c @@ -46,6 +46,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "w32term.h" #endif /* HAVE_NTGUI */ +#ifdef HAVE_NS +#include "nsterm.h" +#endif /* HAVE_NS */ + #ifdef MAC_OS #include "macterm.h" #endif /* MAC_OS */ @@ -57,6 +61,12 @@ Lisp_Object Qopentype; /* Important character set strings. */ Lisp_Object Qascii_0, Qiso8859_1, Qiso10646_1, Qunicode_bmp, Qunicode_sip; +#ifdef HAVE_NS +#define DEFAULT_ENCODING Qiso10646_1 +#else +#define DEFAULT_ENCODING Qiso8859_1 +#endif + /* Special vector of zero length. This is repeatedly used by (struct font_driver *)->list when a specified font is not found. */ static Lisp_Object null_vector; @@ -3096,7 +3106,7 @@ font_find_for_lface (f, attrs, spec, c) registry[0] = AREF (spec, FONT_REGISTRY_INDEX); if (NILP (registry[0])) { - registry[0] = Qiso8859_1; + registry[0] = DEFAULT_ENCODING; registry[1] = Qascii_0; registry[2] = null_vector; } @@ -4948,6 +4958,7 @@ extern void syms_of_ftxfont P_ (()); extern void syms_of_bdffont P_ (()); extern void syms_of_w32font P_ (()); extern void syms_of_atmfont P_ (()); +extern void syms_of_nsfont P_ (()); void syms_of_font () @@ -5117,6 +5128,9 @@ EMACS_FONT_LOG is set. Otherwise, it is set to t. */); #ifdef WINDOWSNT syms_of_w32font (); #endif /* WINDOWSNT */ +#ifdef HAVE_NS + syms_of_nsfont (); +#endif /* HAVE_NS */ #ifdef MAC_OS syms_of_atmfont (); #endif /* MAC_OS */ diff --git a/src/font.h b/src/font.h index c70e87deabc..a6360e1432e 100644 --- a/src/font.h +++ b/src/font.h @@ -860,6 +860,9 @@ extern struct font_driver uniscribe_font_driver; #ifdef MAC_OS extern struct font_driver atmfont_driver; #endif /* MAC_OS */ +#ifdef HAVE_NS +extern struct font_driver nsfont_driver; +#endif /* HAVE_NS */ #ifndef FONT_DEBUG #define FONT_DEBUG diff --git a/src/fontset.c b/src/fontset.c index e4448537458..edcbaa63070 100644 --- a/src/fontset.c +++ b/src/fontset.c @@ -47,6 +47,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #ifdef WINDOWSNT #include "w32term.h" #endif +#ifdef HAVE_NS +#include "nsterm.h" +#endif #ifdef MAC_OS #include "macterm.h" #endif @@ -1622,6 +1625,11 @@ fontset_from_font (font_object) ASET (font_spec, i, Qnil); Fset_fontset_font (name, Qlatin, font_spec, Qnil, Qnil); Fset_fontset_font (name, Qnil, font_spec, Qnil, Qnil); + +#ifdef HAVE_NS + nsfont_make_fontset_for_font(name, font_object); +#endif + return XINT (FONTSET_ID (fontset)); } diff --git a/src/frame.c b/src/frame.c index a4fa29bff49..617d9233889 100644 --- a/src/frame.c +++ b/src/frame.c @@ -32,6 +32,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #ifdef MAC_OS #include "macterm.h" #endif +#ifdef HAVE_NS +#include "nsterm.h" +#endif #include "buffer.h" /* These help us bind and responding to switch-frame events. */ #include "commands.h" @@ -72,7 +75,7 @@ Lisp_Object Vframe_alpha_lower_limit; Lisp_Object Qframep, Qframe_live_p; Lisp_Object Qicon, Qmodeline; Lisp_Object Qonly; -Lisp_Object Qx, Qw32, Qmac, Qpc; +Lisp_Object Qx, Qw32, Qmac, Qpc, Qns; Lisp_Object Qvisible; Lisp_Object Qdisplay_type; Lisp_Object Qbackground_mode; @@ -203,7 +206,8 @@ DEFUN ("framep", Fframep, Sframep, 1, 1, 0, Value is t for a termcap frame (a character-only terminal), `x' for an Emacs frame that is really an X window, `w32' for an Emacs frame that is a window on MS-Windows display, -`mac' for an Emacs frame on a Macintosh display, +`mac' for an Emacs frame on a Macintosh 8/9 X-Carbon display, +`ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display, `pc' for a direct-write MS-DOS frame. See also `frame-live-p'. */) (object) @@ -224,6 +228,8 @@ See also `frame-live-p'. */) return Qpc; case output_mac: return Qmac; + case output_ns: + return Qns; default: abort (); } @@ -551,6 +557,11 @@ make_initial_frame (void) FRAME_CAN_HAVE_SCROLL_BARS (f) = 0; FRAME_VERTICAL_SCROLL_BAR_TYPE (f) = vertical_scroll_bar_none; +#ifdef CANNOT_DUMP + if (!noninteractive) + init_frame_faces (f); +#endif + return f; } @@ -880,6 +891,12 @@ do_switch_frame (frame, track, for_deletion) Fselect_window (XFRAME (frame)->selected_window, Qnil); +#ifdef NS_IMPL_COCOA + /* term gets no other notification of this */ + if (for_deletion) + Fraise_frame(Qnil); +#endif + /* We want to make sure that the next event generates a frame-switch event to the appropriate frame. This seems kludgy to me, but before you take it out, make sure that evaluating something like @@ -2969,8 +2986,9 @@ x_set_frame_parameters (f, alist) old_value = get_frame_param (f, prop); fullscreen_is_being_set |= EQ (prop, Qfullscreen); - +#ifndef HAVE_NS /* PENDING: ensure font attrs change goes through */ if (NILP (Fequal (val, old_value))) +#endif { store_frame_param (f, prop, val); @@ -3949,6 +3967,9 @@ x_get_arg (dpyinfo, alist, param, attribute, class, type) case RES_TYPE_BOOLEAN: tem = Fdowncase (tem); if (!strcmp (SDATA (tem), "on") +#ifdef HAVE_NS + || !strcmp(SDATA(tem), "yes") +#endif || !strcmp (SDATA (tem), "true")) return Qt; else @@ -3964,9 +3985,15 @@ x_get_arg (dpyinfo, alist, param, attribute, class, type) Lisp_Object lower; lower = Fdowncase (tem); if (!strcmp (SDATA (lower), "on") +#ifdef HAVE_NS + || !strcmp(SDATA(lower), "yes") +#endif || !strcmp (SDATA (lower), "true")) return Qt; else if (!strcmp (SDATA (lower), "off") +#ifdef HAVE_NS + || !strcmp(SDATA(lower), "no") +#endif || !strcmp (SDATA (lower), "false")) return Qnil; else @@ -4366,6 +4393,8 @@ syms_of_frame () staticpro (&Qpc); Qmac = intern ("mac"); staticpro (&Qmac); + Qns = intern ("ns"); + staticpro (&Qns); Qvisible = intern ("visible"); staticpro (&Qvisible); Qbuffer_predicate = intern ("buffer-predicate"); @@ -4473,8 +4502,8 @@ Setting this variable does not affect existing frames, only new ones. */); DEFVAR_LISP ("default-frame-scroll-bars", &Vdefault_frame_scroll_bars, doc: /* Default position of scroll bars on this window-system. */); #ifdef HAVE_WINDOW_SYSTEM -#if defined(HAVE_NTGUI) || defined(MAC_OS) - /* MS-Windows has scroll bars on the right by default. */ +#if defined(HAVE_NTGUI) || defined(MAC_OS) || defined(NS_IMPL_COCOA) + /* MS-Windows and Mac OS X have scroll bars on the right by default. */ Vdefault_frame_scroll_bars = Qright; #else Vdefault_frame_scroll_bars = Qleft; @@ -4540,7 +4569,7 @@ You should set this variable to tell Emacs how your window manager handles focus, since there is no way in general for Emacs to find out automatically. */); #ifdef HAVE_WINDOW_SYSTEM -#if defined(HAVE_NTGUI) || defined(MAC_OS) +#if defined(HAVE_NTGUI) || defined(MAC_OS) || defined(HAVE_NS) focus_follows_mouse = 0; #else focus_follows_mouse = 1; diff --git a/src/frame.h b/src/frame.h index 941b9a90a26..b744c9e56b6 100644 --- a/src/frame.h +++ b/src/frame.h @@ -50,7 +50,8 @@ enum output_method output_x_window, output_msdos_raw, output_w32, - output_mac + output_mac, + output_ns }; enum vertical_scroll_bar_type @@ -242,7 +243,7 @@ struct frame auto-resize-tool-bar is set to grow-only. */ unsigned minimize_tool_bar_window_p : 1; -#if defined (USE_GTK) || defined (MAC_OS) +#if defined (USE_GTK) || defined (HAVE_NS) || defined (MAC_OS) /* Nonzero means using a tool bar that comes from the toolkit. */ int external_tool_bar; #endif @@ -332,6 +333,7 @@ struct frame struct x_output *x; /* xterm.h */ struct w32_output *w32; /* w32term.h */ struct mac_output *mac; /* macterm.h */ + struct ns_output *ns; /* nsterm.h */ EMACS_INT nothing; } output_data; @@ -359,7 +361,7 @@ struct frame int menu_bar_lines; #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined (HAVE_NS) || defined (USE_GTK) /* Nonzero means using a menu bar that comes from the X toolkit. */ unsigned int external_menu_bar : 1; #endif @@ -519,6 +521,7 @@ typedef struct frame *FRAME_PTR; #define FRAME_W32_P(f) ((f)->output_method == output_w32) #define FRAME_MSDOS_P(f) ((f)->output_method == output_msdos_raw) #define FRAME_MAC_P(f) ((f)->output_method == output_mac) +#define FRAME_NS_P(f) ((f)->output_method == output_ns) /* FRAME_WINDOW_P tests whether the frame is a window, and is defined to be the predicate for the window system being used. */ @@ -532,6 +535,9 @@ typedef struct frame *FRAME_PTR; #ifdef MAC_OS #define FRAME_WINDOW_P(f) FRAME_MAC_P (f) #endif +#ifdef HAVE_NS +#define FRAME_WINDOW_P(f) FRAME_NS_P(f) +#endif #ifndef FRAME_WINDOW_P #define FRAME_WINDOW_P(f) (0) #endif @@ -570,7 +576,7 @@ typedef struct frame *FRAME_PTR; /* Nonzero if this frame should display a tool bar in a way that does not use any text lines. */ -#if defined (USE_GTK) || defined (MAC_OS) +#if defined (USE_GTK) || defined (HAVE_NS) || defined (MAC_OS) #define FRAME_EXTERNAL_TOOL_BAR(f) (f)->external_tool_bar #else #define FRAME_EXTERNAL_TOOL_BAR(f) 0 @@ -589,7 +595,7 @@ typedef struct frame *FRAME_PTR; /* Nonzero if this frame should display a menu bar in a way that does not use any text lines. */ #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined (HAVE_NS) || defined (USE_GTK) #define FRAME_EXTERNAL_MENU_BAR(f) (f)->external_menu_bar #else #define FRAME_EXTERNAL_MENU_BAR(f) 0 @@ -1044,7 +1050,7 @@ extern Lisp_Object Qleft_fringe, Qright_fringe; extern Lisp_Object Qheight, Qwidth; extern Lisp_Object Qminibuffer, Qmodeline; extern Lisp_Object Qonly; -extern Lisp_Object Qx, Qw32, Qmac, Qpc; +extern Lisp_Object Qx, Qw32, Qmac, Qpc, Qns; extern Lisp_Object Qvisible; extern Lisp_Object Qdisplay_type; extern Lisp_Object Qbackground_mode; diff --git a/src/fringe.c b/src/fringe.c index 57a1861446b..ad40df5a2fd 100644 --- a/src/fringe.c +++ b/src/fringe.c @@ -482,7 +482,7 @@ static struct fringe_bitmap **fringe_bitmaps; static Lisp_Object *fringe_faces; static int max_fringe_bitmaps; -static int max_used_fringe_bitmap = MAX_STANDARD_FRINGE_BITMAPS; +int max_used_fringe_bitmap = MAX_STANDARD_FRINGE_BITMAPS; /* Lookup bitmap number for symbol BITMAP. diff --git a/src/getloadavg.c b/src/getloadavg.c index 32d0f41a4a9..c5d78907d82 100644 --- a/src/getloadavg.c +++ b/src/getloadavg.c @@ -420,7 +420,7 @@ extern int errno; # define host_self mach_host_self # endif -# ifdef NeXT +# if defined(NeXT) || defined(RHAPSODY) || defined(DARWIN) || defined(MAC_OSX) # ifdef HAVE_MACH_MACH_H # include <mach/mach.h> # else @@ -467,7 +467,7 @@ extern int errno; /* Avoid static vars inside a function since in HPUX they dump as pure. */ -# ifdef NeXT +# if defined(NeXT) || defined(RHAPSODY) || defined(DARWIN) || defined(MAC_OSX) static processor_set_t default_set; static int getloadavg_initialized; # endif /* NeXT */ @@ -647,7 +647,8 @@ getloadavg (loadavg, nelem) # endif /* __NetBSD__ */ -# if !defined (LDAV_DONE) && defined (NeXT) +# if !defined (LDAV_DONE) && ( defined (NeXT) || defined(RHAPSODY) \ + || defined(DARWIN) || defined(MAC_OSX) ) # define LDAV_DONE /* The NeXT code was adapted from iscreen 3.2. */ diff --git a/src/image.c b/src/image.c index 652c738d109..1d73704b388 100644 --- a/src/image.c +++ b/src/image.c @@ -131,6 +131,32 @@ typedef struct mac_bitmap_record Bitmap_Record; #endif /* MAC_OS */ +#ifdef HAVE_NS +#include "nsterm.h" +#include <sys/types.h> +#include <sys/stat.h> + +#undef COLOR_TABLE_SUPPORT + +typedef struct ns_bitmap_record Bitmap_Record; + +#define GET_PIXEL(ximg, x, y) XGetPixel(ximg, x, y) +#define NO_PIXMAP 0 + +#define RGB_PIXEL_COLOR unsigned long +#define ZPixmap 0 + +#define PIX_MASK_RETAIN 0 +#define PIX_MASK_DRAW 1 + +#define FRAME_X_VISUAL FRAME_NS_DISPLAY_INFO(f)->visual +#define x_defined_color(f, name, color_def, alloc) \ + ns_defined_color (f, name, color_def, alloc, 0) +#define FRAME_X_SCREEN(f) 0 +#define DefaultDepthOfScreen(screen) ns_display_list->n_planes +#endif /* HAVE_NS */ + + /* Search path for bitmap files. */ Lisp_Object Vx_bitmap_file_path; @@ -403,6 +429,33 @@ mac_create_cg_image_from_image (f, img) #endif /* USE_CG_DRAWING */ #endif /* MAC_OS */ +#ifdef HAVE_NS +XImagePtr +XGetImage (Display *display, Pixmap pixmap, int x, int y, + unsigned int width, unsigned int height, + unsigned long plane_mask, int format) +{ + /* PENDING: not sure what this function is supposed to do.. */ + ns_retain_object(pixmap); + return pixmap; +} + +/* use with imgs created by ns_image_for_XPM */ +unsigned long +XGetPixel (XImagePtr ximage, int x, int y) +{ + return ns_get_pixel(ximage, x, y); +} + +/* use with imgs created by ns_image_for_XPM; alpha set to 1; + pixel is assumed to be in form RGB */ +void +XPutPixel (XImagePtr ximage, int x, int y, unsigned long pixel) +{ + ns_put_pixel(ximage, x, y, pixel); +} +#endif /* HAVE_NS */ + /* Functions to access the contents of a bitmap, given an id. */ @@ -519,12 +572,23 @@ x_create_bitmap_from_data (f, bits, width, height) return -1; #endif +#ifdef HAVE_NS + void *bitmap = ns_image_from_XBM(bits, width, height); + if (!bitmap) + return -1; +#endif + id = x_allocate_bitmap_record (f); #ifdef MAC_OS dpyinfo->bitmaps[id - 1].bitmap_data = (char *) xmalloc (height * width); bcopy (bits, dpyinfo->bitmaps[id - 1].bitmap_data, height * width); #endif /* MAC_OS */ +#ifdef HAVE_NS + dpyinfo->bitmaps[id - 1].img = bitmap; + dpyinfo->bitmaps[id - 1].depth = 1; +#endif + dpyinfo->bitmaps[id - 1].file = NULL; dpyinfo->bitmaps[id - 1].height = height; dpyinfo->bitmaps[id - 1].width = width; @@ -552,6 +616,8 @@ x_create_bitmap_from_file (f, file) struct frame *f; Lisp_Object file; { + Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + #ifdef MAC_OS return -1; /* MAC_TODO : bitmap support */ #endif /* MAC_OS */ @@ -560,8 +626,26 @@ x_create_bitmap_from_file (f, file) return -1; /* W32_TODO : bitmap support */ #endif /* HAVE_NTGUI */ +#ifdef HAVE_NS + int id; + void *bitmap = ns_image_from_file(file); + + if (!bitmap) + return -1; + + + id = x_allocate_bitmap_record (f); + dpyinfo->bitmaps[id - 1].img = bitmap; + dpyinfo->bitmaps[id - 1].refcount = 1; + dpyinfo->bitmaps[id - 1].file = (char *) xmalloc (SBYTES (file) + 1); + dpyinfo->bitmaps[id - 1].depth = 1; + dpyinfo->bitmaps[id - 1].height = ns_image_width(bitmap); + dpyinfo->bitmaps[id - 1].width = ns_image_height(bitmap); + strcpy (dpyinfo->bitmaps[id - 1].file, SDATA (file)); + return id; +#endif + #ifdef HAVE_X_WINDOWS - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); unsigned int width, height; Pixmap bitmap; int xhot, yhot, result, id; @@ -630,6 +714,10 @@ free_bitmap_record (dpyinfo, bm) bm->bitmap_data = NULL; #endif /* MAC_OS */ +#ifdef HAVE_NS + ns_release_object(bm->img); +#endif + if (bm->file) { xfree (bm->file); @@ -1407,7 +1495,8 @@ image_ascent (img, face, slice) because a typical font is `top-heavy' (due to the presence uppercase letters), so the image placement should err towards being top-heavy too. It also just generally looks better. */ - ascent = (height + face->font->ascent - face->font->descent + 1) / 2; + ascent = (height + FONT_BASE(face->font) + - FONT_DESCENT(face->font) + 1) / 2; #endif /* HAVE_NTGUI */ } else @@ -1476,6 +1565,14 @@ four_corners_best (ximg, corners, width, height) #define Free_Pixmap(display, pixmap) \ DeleteObject (pixmap) +#elif defined (HAVE_NS) + +#define Destroy_Image(ximg, dummy) \ + ns_release_object(ximg) + +#define Free_Pixmap(display, pixmap) \ + ns_release_object(pixmap) + #else #define Destroy_Image(ximg, dummy) \ @@ -1484,7 +1581,7 @@ four_corners_best (ximg, corners, width, height) #define Free_Pixmap(display, pixmap) \ XFreePixmap (display, pixmap) -#endif /* HAVE_NTGUI */ +#endif /* !HAVE_NTGUI && !HAVE_NS */ /* Return the `background' field of IMG. If IMG doesn't have one yet, @@ -1607,6 +1704,10 @@ x_clear_image_1 (f, img, pixmap_p, mask_p, colors_p) { Free_Pixmap (FRAME_X_DISPLAY (f), img->pixmap); img->pixmap = NO_PIXMAP; +#ifdef HAVE_NS + if (img->background_valid) + ns_free_indexed_color(img->background); +#endif img->background_valid = 0; } @@ -2387,6 +2488,18 @@ x_create_x_image_and_pixmap (f, width, height, depth, ximg, pixmap) return 1; #endif /* MAC_OS */ + +#ifdef HAVE_NS + *pixmap = ns_image_for_XPM(width, height, depth); + if (*pixmap == 0) + { + *ximg = NULL; + image_error ("Unable to allocate NSImage for XPM pixmap", Qnil, Qnil); + return 0; + } + *ximg = *pixmap; + return 1; +#endif } @@ -2412,6 +2525,9 @@ x_destroy_x_image (ximg) #ifdef MAC_OS XDestroyImage (ximg); #endif /* MAC_OS */ +#ifdef HAVE_NS + ns_release_object(ximg); +#endif /* HAVE_NS */ } } @@ -2446,6 +2562,11 @@ x_put_x_image (f, ximg, pixmap, width, height) #ifdef MAC_OS xassert (ximg == pixmap); #endif /* MAC_OS */ + +#ifdef HAVE_NS + xassert (ximg == pixmap); + ns_retain_object(ximg); +#endif } @@ -3499,6 +3620,10 @@ Create_Pixmap_From_Bitmap_Data (f, img, data, fg, bg, non_default_colors) /* If colors were specified, transfer the bitmap to a color one. */ if (non_default_colors) convert_mono_to_color_image (f, img, fg, bg); + +#elif defined (HAVE_NS) + img->pixmap = ns_image_from_XBM(data, img->width, img->height); + #else img->pixmap = XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), @@ -3507,7 +3632,7 @@ Create_Pixmap_From_Bitmap_Data (f, img, data, fg, bg, non_default_colors) img->width, img->height, fg, bg, DefaultDepthOfScreen (FRAME_X_SCREEN (f))); -#endif /* HAVE_NTGUI */ +#endif /* !HAVE_NTGUI && !HAVE_NS */ } @@ -3891,13 +4016,13 @@ xbm_load (f, img) XPM images ***********************************************************************/ -#if defined (HAVE_XPM) || defined (MAC_OS) +#if defined (HAVE_XPM) || defined (MAC_OS) || defined (HAVE_NS) static int xpm_image_p P_ ((Lisp_Object object)); static int xpm_load P_ ((struct frame *f, struct image *img)); static int xpm_valid_color_symbols_p P_ ((Lisp_Object)); -#endif /* HAVE_XPM || MAC_OS */ +#endif /* HAVE_XPM || MAC_OS || HAVE_NS */ #ifdef HAVE_XPM #ifdef HAVE_NTGUI @@ -3920,7 +4045,7 @@ static int xpm_valid_color_symbols_p P_ ((Lisp_Object)); #endif /* HAVE_NTGUI */ #endif /* HAVE_XPM */ -#if defined (HAVE_XPM) || defined (MAC_OS) +#if defined (HAVE_XPM) || defined (MAC_OS) || defined (HAVE_NS) /* The symbol `xpm' identifying XPM-format images. */ Lisp_Object Qxpm; @@ -4247,7 +4372,7 @@ xpm_image_p (object) || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value))); } -#endif /* HAVE_XPM || MAC_OS */ +#endif /* HAVE_XPM || MAC_OS || HAVE_NS */ #if defined (HAVE_XPM) && defined (HAVE_X_WINDOWS) int @@ -4526,7 +4651,7 @@ xpm_load (f, img) #endif /* HAVE_XPM */ -#ifdef MAC_OS +#if defined (MAC_OS) || ( defined (HAVE_NS) && !defined (HAVE_XPM) ) /* XPM support functions for Mac OS where libxpm is not available. Only XPM version 3 (without any extensions) is supported. */ @@ -4884,8 +5009,11 @@ xpm_load_image (f, img, contents, end) if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap) +#ifndef HAVE_NS || !x_create_x_image_and_pixmap (f, width, height, 1, - &mask_img, &img->mask)) + &mask_img, &img->mask) +#endif + ) { image_error ("Out of memory (%s)", img->spec, Qnil); goto error; @@ -4905,9 +5033,14 @@ xpm_load_image (f, img, contents, end) XPutPixel (ximg, x, y, (INTEGERP (color_val) ? XINT (color_val) : FRAME_FOREGROUND_PIXEL (f))); +#ifndef HAVE_NS XPutPixel (mask_img, x, y, (!EQ (color_val, Qt) ? PIX_MASK_DRAW : (have_mask = 1, PIX_MASK_RETAIN))); +#else + if (EQ(color_val, Qt)) + ns_set_alpha(ximg, x, y, 0); +#endif } if (y + 1 < height) expect (','); @@ -4922,6 +5055,7 @@ xpm_load_image (f, img, contents, end) x_put_x_image (f, ximg, img->pixmap, width, height); x_destroy_x_image (ximg); +#ifndef HAVE_NS if (have_mask) { /* Fill in the background_transparent field while we have the @@ -4937,7 +5071,7 @@ xpm_load_image (f, img, contents, end) Free_Pixmap (FRAME_X_DISPLAY (f), img->mask); img->mask = NO_PIXMAP; } - +#endif return 1; failure: @@ -5003,7 +5137,7 @@ xpm_load (f, img) return success_p; } -#endif /* MAC_OS */ +#endif /* MAC_OS || (HAVE_NS && !HAVE_XPM) */ @@ -5273,6 +5407,9 @@ lookup_rgb_color (f, r, g, b) pixel = PALETTERGB (r >> 8, g >> 8, b >> 8); #endif /* HAVE_NTGUI */ +#ifdef HAVE_NS + pixel = RGB_TO_ULONG (r >> 8, g >> 8, b >> 8); +#endif /* HAVE_NS */ return pixel; } @@ -5378,7 +5515,7 @@ x_to_xcolors (f, img, rgb_p) p->pixel = GET_PIXEL (ximg, x, y); if (rgb_p) { -#ifdef MAC_OS +#if defined (MAC_OS) || defined (HAVE_NS) p->red = RED16_FROM_ULONG (p->pixel); p->green = GREEN16_FROM_ULONG (p->pixel); p->blue = BLUE16_FROM_ULONG (p->pixel); @@ -5459,8 +5596,8 @@ x_from_xcolors (f, img, colors) XColor *colors; { int x, y; - XImagePtr oimg; - Pixmap pixmap; + XImagePtr oimg = NULL; + Pixmap pixmap = NULL; XColor *p; init_color_table (); @@ -5673,6 +5810,8 @@ x_disable_image (f, img) Display *dpy = FRAME_X_DISPLAY (f); GC gc; +#ifndef HAVE_NS //TODO: NS support, however this not needed for toolbars + #ifdef MAC_OS #define MaskForeground(f) PIX_MASK_DRAW #else @@ -5697,6 +5836,7 @@ x_disable_image (f, img) img->width - 1, 0); XFreeGC (dpy, gc); } +#endif /* !HAVE_NS */ #else HDC hdc, bmpdc; HGDIOBJ prev; @@ -5762,11 +5902,13 @@ x_build_heuristic_mask (f, img, how) } #ifndef HAVE_NTGUI +#ifndef HAVE_NS /* Create an image and pixmap serving as mask. */ rc = x_create_x_image_and_pixmap (f, img->width, img->height, 1, &mask_img, &img->mask); if (!rc) return 0; +#endif /* !HAVE_NS */ /* Get the X image of IMG->pixmap. */ ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap, 0, 0, @@ -5820,16 +5962,21 @@ x_build_heuristic_mask (f, img, how) #ifndef HAVE_NTGUI for (y = 0; y < img->height; ++y) for (x = 0; x < img->width; ++x) +#ifndef HAVE_NS XPutPixel (mask_img, x, y, (XGetPixel (ximg, x, y) != bg ? PIX_MASK_DRAW : PIX_MASK_RETAIN)); - +#else + if (XGetPixel (ximg, x, y) == bg) + ns_set_alpha(ximg, x, y, 0); +#endif /* HAVE_NS */ +#ifndef HAVE_NS /* Fill in the background_transparent field while we have the mask handy. */ image_background_transparent (img, f, mask_img); /* Put mask_img into img->mask. */ x_put_x_image (f, mask_img, img->mask, img->width, img->height); x_destroy_x_image (mask_img); - +#endif /* !HAVE_NS */ #else for (y = 0; y < img->height; ++y) for (x = 0; x < img->width; ++x) @@ -6280,7 +6427,7 @@ pbm_load (f, img) PNG ***********************************************************************/ -#if defined (HAVE_PNG) || defined (MAC_OS) +#if defined (HAVE_PNG) || defined (MAC_OS) || defined (HAVE_NS) /* Function prototypes. */ @@ -6352,7 +6499,7 @@ png_image_p (object) return fmt[PNG_FILE].count + fmt[PNG_DATA].count == 1; } -#endif /* HAVE_PNG || MAC_OS */ +#endif /* HAVE_PNG || MAC_OS || HAVE_NS */ #ifdef HAVE_PNG @@ -6912,6 +7059,17 @@ png_load (f, img) } #endif /* MAC_OS */ +#ifdef HAVE_NS +static int +png_load (struct frame *f, struct image *img) +{ + return ns_load_image(f, img, + image_spec_value (img->spec, QCfile, NULL), + image_spec_value (img->spec, QCdata, NULL)); +} +#endif /* HAVE_NS */ + + #endif /* !HAVE_PNG */ @@ -6920,7 +7078,7 @@ png_load (f, img) JPEG ***********************************************************************/ -#if defined (HAVE_JPEG) || defined (MAC_OS) +#if defined (HAVE_JPEG) || defined (MAC_OS) || defined (HAVE_NS) static int jpeg_image_p P_ ((Lisp_Object object)); static int jpeg_load P_ ((struct frame *f, struct image *img)); @@ -6991,7 +7149,7 @@ jpeg_image_p (object) return fmt[JPEG_FILE].count + fmt[JPEG_DATA].count == 1; } -#endif /* HAVE_JPEG || MAC_OS */ +#endif /* HAVE_JPEG || MAC_OS || HAVE_NS */ #ifdef HAVE_JPEG @@ -7491,6 +7649,16 @@ jpeg_load (f, img) } #endif /* MAC_OS */ +#ifdef HAVE_NS +static int +jpeg_load (struct frame *f, struct image *img) +{ + return ns_load_image(f, img, + image_spec_value (img->spec, QCfile, NULL), + image_spec_value (img->spec, QCdata, NULL)); +} +#endif /* HAVE_NS */ + #endif /* !HAVE_JPEG */ @@ -7499,7 +7667,7 @@ jpeg_load (f, img) TIFF ***********************************************************************/ -#if defined (HAVE_TIFF) || defined (MAC_OS) +#if defined (HAVE_TIFF) || defined (MAC_OS) || defined (HAVE_NS) static int tiff_image_p P_ ((Lisp_Object object)); static int tiff_load P_ ((struct frame *f, struct image *img)); @@ -7569,7 +7737,7 @@ tiff_image_p (object) return fmt[TIFF_FILE].count + fmt[TIFF_DATA].count == 1; } -#endif /* HAVE_TIFF || MAC_OS */ +#endif /* HAVE_TIFF || MAC_OS || HAVE_NS */ #ifdef HAVE_TIFF @@ -7916,6 +8084,16 @@ tiff_load (f, img) } #endif /* MAC_OS */ +#ifdef HAVE_NS +static int +tiff_load (struct frame *f, struct image *img) +{ + return ns_load_image(f, img, + image_spec_value (img->spec, QCfile, NULL), + image_spec_value (img->spec, QCdata, NULL)); +} +#endif /* HAVE_NS */ + #endif /* !HAVE_TIFF */ @@ -7924,7 +8102,7 @@ tiff_load (f, img) GIF ***********************************************************************/ -#if defined (HAVE_GIF) || defined (MAC_OS) +#if defined (HAVE_GIF) || defined (MAC_OS) || defined (HAVE_NS) static int gif_image_p P_ ((Lisp_Object object)); static int gif_load P_ ((struct frame *f, struct image *img)); @@ -8553,6 +8731,16 @@ gif_load (f, img) } #endif /* MAC_OS */ +#ifdef HAVE_NS +static int +gif_load (struct frame *f, struct image *img) +{ + return ns_load_image(f, img, + image_spec_value (img->spec, QCfile, NULL), + image_spec_value (img->spec, QCdata, NULL)); +} +#endif /* HAVE_NS */ + #endif /* HAVE_GIF */ @@ -9335,27 +9523,27 @@ of `image-library-alist', which see). */) if (CONSP (tested)) return XCDR (tested); -#if defined (HAVE_XPM) || defined (MAC_OS) +#if defined (HAVE_XPM) || defined (MAC_OS) || defined (HAVE_NS) if (EQ (type, Qxpm)) return CHECK_LIB_AVAILABLE (&xpm_type, init_xpm_functions, libraries); #endif -#if defined (HAVE_JPEG) || defined (MAC_OS) +#if defined (HAVE_JPEG) || defined (MAC_OS) || defined (HAVE_NS) if (EQ (type, Qjpeg)) return CHECK_LIB_AVAILABLE (&jpeg_type, init_jpeg_functions, libraries); #endif -#if defined (HAVE_TIFF) || defined (MAC_OS) +#if defined (HAVE_TIFF) || defined (MAC_OS) || defined (HAVE_NS) if (EQ (type, Qtiff)) return CHECK_LIB_AVAILABLE (&tiff_type, init_tiff_functions, libraries); #endif -#if defined (HAVE_GIF) || defined (MAC_OS) +#if defined (HAVE_GIF) || defined (MAC_OS) || defined (HAVE_NS) if (EQ (type, Qgif)) return CHECK_LIB_AVAILABLE (&gif_type, init_gif_functions, libraries); #endif -#if defined (HAVE_PNG) || defined (MAC_OS) +#if defined (HAVE_PNG) || defined (MAC_OS) || defined (HAVE_NS) if (EQ (type, Qpng)) return CHECK_LIB_AVAILABLE (&png_type, init_png_functions, libraries); #endif @@ -9480,31 +9668,31 @@ non-numeric, there is no explicit limit on the size of images. */); staticpro (&QCpt_height); #endif /* HAVE_GHOSTSCRIPT */ -#if defined (HAVE_XPM) || defined (MAC_OS) +#if defined (HAVE_XPM) || defined (MAC_OS) || defined (HAVE_NS) Qxpm = intern ("xpm"); staticpro (&Qxpm); ADD_IMAGE_TYPE (Qxpm); #endif -#if defined (HAVE_JPEG) || defined (MAC_OS) +#if defined (HAVE_JPEG) || defined (MAC_OS) || defined (HAVE_NS) Qjpeg = intern ("jpeg"); staticpro (&Qjpeg); ADD_IMAGE_TYPE (Qjpeg); #endif -#if defined (HAVE_TIFF) || defined (MAC_OS) +#if defined (HAVE_TIFF) || defined (MAC_OS) || defined (HAVE_NS) Qtiff = intern ("tiff"); staticpro (&Qtiff); ADD_IMAGE_TYPE (Qtiff); #endif -#if defined (HAVE_GIF) || defined (MAC_OS) +#if defined (HAVE_GIF) || defined (MAC_OS) || defined (HAVE_NS) Qgif = intern ("gif"); staticpro (&Qgif); ADD_IMAGE_TYPE (Qgif); #endif -#if defined (HAVE_PNG) || defined (MAC_OS) +#if defined (HAVE_PNG) || defined (MAC_OS) || defined (HAVE_NS) Qpng = intern ("png"); staticpro (&Qpng); ADD_IMAGE_TYPE (Qpng); diff --git a/src/keyboard.c b/src/keyboard.c index e42e17525e6..b908b8ad349 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -80,6 +80,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "macterm.h" #endif +#ifdef HAVE_NS +#include "nsterm.h" +extern Lisp_Object Qsuper; +#endif + #ifndef USE_CRT_DLL extern int errno; #endif @@ -4147,7 +4152,8 @@ kbd_buffer_get_event (kbp, used_mouse_menu, end_time) #endif } -#if defined (HAVE_X11) || defined (HAVE_NTGUI) || defined (MAC_OS) +#if defined (HAVE_X11) || defined (HAVE_NTGUI) || defined (MAC_OS) \ + || defined (HAVE_NS) else if (event->kind == DELETE_WINDOW_EVENT) { /* Make an event (delete-frame (FRAME)). */ @@ -4156,7 +4162,8 @@ kbd_buffer_get_event (kbp, used_mouse_menu, end_time) kbd_fetch_ptr = event + 1; } #endif -#if defined (HAVE_X11) || defined (HAVE_NTGUI) || defined (MAC_OS) +#if defined (HAVE_X11) || defined (HAVE_NTGUI) || defined (MAC_OS) \ + || defined (HAVE_NS) else if (event->kind == ICONIFY_EVENT) { /* Make an event (iconify-frame (FRAME)). */ @@ -4179,7 +4186,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu, end_time) kbd_fetch_ptr = event + 1; } #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined(HAVE_NS) || defined (USE_GTK) else if (event->kind == MENU_BAR_ACTIVATE_EVENT) { kbd_fetch_ptr = event + 1; @@ -4289,7 +4296,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu, end_time) obj = make_lispy_event (event); #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined(MAC_OS) \ - || defined (USE_GTK) + || defined(HAVE_NS) || defined (USE_GTK) /* If this was a menu selection, then set the flag to inhibit writing to last_nonmenu_event. Don't do this if the event we're returning is (menu-bar), though; that indicates the @@ -5643,7 +5650,7 @@ make_lispy_event (event) if (event->kind == MOUSE_CLICK_EVENT) { struct frame *f = XFRAME (event->frame_or_window); -#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK) +#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK) && ! defined (HAVE_NS) int row, column; #endif @@ -5652,7 +5659,7 @@ make_lispy_event (event) if (! FRAME_LIVE_P (f)) return Qnil; -#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK) +#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK) && ! defined (HAVE_NS) /* EVENT->x and EVENT->y are frame-relative pixel coordinates at this place. Under old redisplay, COLUMN and ROW are set to frame relative glyph coordinates @@ -5712,7 +5719,7 @@ make_lispy_event (event) return Fcons (item, Fcons (position, Qnil)); } -#endif /* not USE_X_TOOLKIT && not USE_GTK */ +#endif /* not USE_X_TOOLKIT && not USE_GTK && not HAVE_NS */ position = make_lispy_position (f, &event->x, &event->y, event->timestamp); @@ -6092,7 +6099,7 @@ make_lispy_event (event) #endif /* HAVE_MOUSE */ #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined(HAVE_NS) || defined (USE_GTK) case MENU_BAR_EVENT: if (EQ (event->arg, event->frame_or_window)) /* This is the prefix key. We translate this to @@ -7305,6 +7312,10 @@ tty_read_avail_input (struct terminal *terminal, void handle_async_input () { +#ifdef BSD4_1 + extern int select_alarmed; +#endif + interrupt_input_pending = 0; while (1) @@ -7317,6 +7328,9 @@ handle_async_input () if (nread <= 0) break; +#ifdef BSD4_1 + select_alarmed = 1; /* Force the select emulator back to life */ +#endif } } @@ -7335,6 +7349,10 @@ input_available_signal (signo) signal (signo, input_available_signal); #endif /* USG */ +#ifdef BSD4_1 + sigisheld (SIGIO); +#endif + #ifdef SYNC_INPUT interrupt_input_pending = 1; #else @@ -7348,6 +7366,9 @@ input_available_signal (signo) handle_async_input (); #endif +#ifdef BSD4_1 + sigfree (); +#endif errno = old_errno; } #endif /* SIGIO */ @@ -7975,10 +7996,15 @@ parse_menu_item (item, notreal, inmenubar) /* With the introduction of where_is_cache, the computation of equivalent key bindings is sufficiently fast that we do not need to cache it here any more. */ - /* CHECK_IMPURE (start); - XSETCDR (start, Fcons (Fcons (Qnil, Qnil), XCDR (start))); - cachelist = XCAR (XCDR (start)); */ +/*PENDING: under NS this effect does not hold, perhaps due to the + modifier-preference changes to where-is-internal.. */ +#ifdef HAVE_NS + CHECK_IMPURE (start); + XSETCDR (start, Fcons (Fcons (Qnil, Qnil), XCDR (start))); + cachelist = XCAR (XCDR (start)); +#else cachelist = Fcons (Qnil, Qnil); +#endif newcache = 1; tem = AREF (item_properties, ITEM_PROPERTY_KEYEQ); if (!NILP (keyhint)) @@ -8044,7 +8070,12 @@ parse_menu_item (item, notreal, inmenubar) && SYMBOLP (XSYMBOL (def)->function) && ! NILP (Fget (def, Qmenu_alias))) def = XSYMBOL (def)->function; +#ifdef HAVE_NS + /* prefer 'super' bindings */ + tem = Fwhere_is_internal (def, Qnil, Qsuper, Qt, Qt); +#else tem = Fwhere_is_internal (def, Qnil, Qt, Qnil, Qt); +#endif XSETCAR (cachelist, tem); if (NILP (tem)) { @@ -8077,7 +8108,7 @@ parse_menu_item (item, notreal, inmenubar) if (newcache && !NILP (tem)) { tem = concat2 (build_string (" "), tem); - // tem = concat3 (build_string (" ("), tem, build_string (")")); + /* tem = concat3 (build_string (" ("), tem, build_string (")")); */ XSETCDR (cachelist, tem); } diff --git a/src/keyboard.h b/src/keyboard.h index 2f0f434909a..e21766273a8 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -314,8 +314,7 @@ extern Lisp_Object unuse_menu_items P_ ((Lisp_Object dummy)); confined to an extended version of this with sections of code below using it unconditionally. */ #ifndef HAVE_NTGUI -#ifdef USE_GTK -/* gtk just uses utf-8. */ +#if defined (USE_GTK) || defined (HAVE_NS) # define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str) #elif defined HAVE_X_I18N #define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str) @@ -325,6 +324,56 @@ extern Lisp_Object unuse_menu_items P_ ((Lisp_Object dummy)); #else /* HAVE_NTGUI */ #define ENCODE_MENU_STRING(str) (str) #endif + +#if defined (HAVE_NS) || defined (HAVE_NTGUI) + +typedef void * XtPointer; +typedef unsigned char Boolean; + +/* Definitions copied from lwlib.h */ + +enum button_type +{ + BUTTON_TYPE_NONE, + BUTTON_TYPE_TOGGLE, + BUTTON_TYPE_RADIO +}; + +/* This structure is based on the one in ../lwlib/lwlib.h, with unused portions + removed. No term uses these. */ +typedef struct _widget_value +{ + /* name of widget */ + Lisp_Object lname; + char* name; + /* value (meaning depend on widget type) */ + char* value; + /* keyboard equivalent. no implications for XtTranslations */ + Lisp_Object lkey; + char* key; + /* Help string or nil if none. + GC finds this string through the frame's menu_bar_vector + or through menu_items. */ + Lisp_Object help; + /* true if enabled */ + Boolean enabled; + /* true if selected */ + Boolean selected; + /* The type of a button. */ + enum button_type button_type; +#if defined (HAVE_NTGUI) + /* true if menu title */ + Boolean title; +#endif + /* Contents of the sub-widgets, also selected slot for checkbox */ + struct _widget_value* contents; + /* data passed to callback */ + XtPointer call_data; + /* next one in the list */ + struct _widget_value* next; +} widget_value; +#endif + /* Macros for dealing with lispy events. */ diff --git a/src/keymap.c b/src/keymap.c index 7a9b5949151..545ab3de81e 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -111,6 +111,10 @@ extern Lisp_Object meta_prefix_char; extern Lisp_Object Voverriding_local_map; +#ifdef HAVE_NS +extern Lisp_Object Qalt, Qcontrol, Qhyper, Qmeta, Qsuper; +#endif + /* Hash table used to cache a reverse-map to speed up calls to where-is. */ static Lisp_Object where_is_cache; /* Which keymaps are reverse-stored in the cache. */ @@ -2621,6 +2625,41 @@ ascii_sequence_p (seq) return 1; } +#ifdef HAVE_NS +int lisp_to_mod(Lisp_Object lmod) +/* ------------------------------------------------------------------------- + Convert lisp symbol to emacs modifier code. + ------------------------------------------------------------------------- */ +{ + if (EQ(lmod, Qmeta)) + return meta_modifier; + else if (EQ(lmod, Qsuper)) + return super_modifier; + else if (EQ(lmod, Qcontrol)) + return ctrl_modifier; + else if (EQ(lmod, Qalt)) + return alt_modifier; + else if (EQ(lmod, Qhyper)) + return hyper_modifier; + return 0; +} + +/* Return non-zero if SEQ starts w/a char modified by given modifier only. */ +static int +modifier_sequence_p (Lisp_Object seq, Lisp_Object modifier) +{ + Lisp_Object idx, elt; + + if (XINT (Flength (seq)) == 0) + return 0; + XSETFASTINT(idx, 0); + elt = Faref(seq, idx); + + return (XUINT(elt) & (CHAR_MODIFIER_MASK ^ shift_modifier)) + == lisp_to_mod(modifier); +} +#endif + /* where-is - finding a command in a set of keymaps. */ @@ -2803,6 +2842,14 @@ where_is_internal (definition, keymaps, firstonly, noindirect, no_remap) we find. */ if (EQ (firstonly, Qnon_ascii)) RETURN_UNGCPRO (sequence); +#ifdef HAVE_NS + /* respond to modifier preference */ + else if ((EQ (firstonly, Qalt) || EQ (firstonly, Qcontrol) + || EQ (firstonly, Qhyper) || EQ (firstonly, Qmeta) + || EQ (firstonly, Qsuper))) + if (modifier_sequence_p(sequence, firstonly)) + RETURN_UNGCPRO (sequence); +#endif else if (!NILP (firstonly) && ascii_sequence_p (sequence)) RETURN_UNGCPRO (sequence); @@ -2836,6 +2883,10 @@ If KEYMAP is a list of keymaps, search only those keymaps. If optional 3rd arg FIRSTONLY is non-nil, return the first key sequence found, rather than a list of all possible key sequences. +#ifdef HAVE_NS +If FIRSTONLY is the symbol for a modifier key, return the first binding found, +that is modified by that modifier only. +#endif If FIRSTONLY is the symbol `non-ascii', return the first binding found, no matter what it is. If FIRSTONLY has another non-nil value, prefer sequences of ASCII characters @@ -2909,10 +2960,19 @@ remapped command in the returned list. */) for (i = n - 1; i >= 0; --i) if (EQ (shadow_lookup (keymaps, defns[i], Qnil), definition)) { - if (ascii_sequence_p (defns[i])) - break; - else if (j < 0) - j = i; +#ifdef HAVE_NS + if ((EQ (firstonly, Qalt) || EQ (firstonly, Qcontrol) + || EQ (firstonly, Qhyper) || EQ (firstonly, Qmeta) + || EQ (firstonly, Qsuper)) + && modifier_sequence_p(defns[i], firstonly)) + break; + else if (EQ (firstonly, Qt) && ascii_sequence_p (defns[i])) +#else + if (ascii_sequence_p (defns[i])) +#endif + break; + else if (j < 0) + j = i; } result = i >= 0 ? defns[i] : (j >= 0 ? defns[j] : Qnil); diff --git a/src/lisp.h b/src/lisp.h index 5216b7a72f8..366a0be146d 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -28,6 +28,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #define P_(proto) () #endif +#ifdef NS_IMPL_GNUSTEP +/* This conflicts with functions in the GNUstep libraries. */ +#define hash_remove emacs_hash_remove +#endif /* NS_IMPL_GNUSTEP */ + #if 0 /* Define this temporarily to hunt a bug. If defined, the size of strings is redundantly recorded in sdata structures so that it can @@ -157,7 +162,7 @@ extern void die P_((const char *, const char *, int)) NO_RETURN; #endif /* Let's USE_LSB_TAG on systems where we know malloc returns mult-of-8. */ -#if defined GNU_MALLOC || defined DOUG_LEA_MALLOC || defined __GLIBC__ || defined MAC_OSX +#if defined GNU_MALLOC || defined DOUG_LEA_MALLOC || defined __GLIBC__ || defined MAC_OSX || defined(NS_IMPL_COCOA) /* We also need to be able to specify mult-of-8 alignment on static vars. */ # if defined DECL_ALIGN /* We currently do not support USE_LSB_TAG with a union Lisp_Object. */ diff --git a/src/lread.c b/src/lread.c index 6415448c0bc..6ec8dff6cfd 100644 --- a/src/lread.c +++ b/src/lread.c @@ -4095,7 +4095,7 @@ init_lread () } #endif -#if (!(defined(WINDOWSNT) || (defined(HAVE_CARBON)))) +#if (!(defined(WINDOWSNT) || (defined(HAVE_CARBON)) || (defined(HAVE_NS)))) /* When Emacs is invoked over network shares on NT, PATH_LOADSEARCH is almost never correct, thereby causing a warning to be printed out that confuses users. Since PATH_LOADSEARCH is always overridden by the @@ -4125,7 +4125,7 @@ init_lread () } } } -#endif /* !(WINDOWSNT || HAVE_CARBON) */ +#endif /* !(WINDOWSNT || HAVE_CARBON || HAVE_NS) */ /* If the EMACSLOADPATH environment variable is set, use its value. This doesn't apply if we're dumping. */ diff --git a/src/menu.c b/src/menu.c index e8bc8de1bfc..969225d4c72 100644 --- a/src/menu.c +++ b/src/menu.c @@ -36,6 +36,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "xterm.h" #endif +#ifdef HAVE_NS +#include "nsterm.h" +#endif + #ifdef USE_GTK #include "gtkutil.h" #endif @@ -440,7 +444,7 @@ single_menu_item (key, item, dummy, skp_v) XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED], XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]); -#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || defined (HAVE_NTGUI) /* Display a submenu using the toolkit. */ if (! (NILP (map) || NILP (enabled))) { @@ -580,7 +584,7 @@ parse_single_submenu (item_key, item_name, maps) } -#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || defined (HAVE_NTGUI) /* Allocate a widget_value, blocking input. */ @@ -956,7 +960,71 @@ find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data) } } -#endif /* USE_X_TOOLKIT || USE_GTK || HAVE_NTGUI */ +#endif /* USE_X_TOOLKIT || USE_GTK || HAVE_NS || HAVE_NTGUI */ + +#ifdef HAVE_NS +/* As above, but return the menu selection instead of storing in kb buffer. + If keymaps==1, return full prefixes to selection. */ +Lisp_Object +find_and_return_menu_selection (FRAME_PTR f, int keymaps, void *client_data) +{ + Lisp_Object prefix, entry; + int i; + Lisp_Object *subprefix_stack; + int submenu_depth = 0; + + prefix = entry = Qnil; + i = 0; + subprefix_stack = + (Lisp_Object *)alloca(menu_items_used * sizeof (Lisp_Object)); + + while (i < menu_items_used) + { + if (EQ (XVECTOR (menu_items)->contents[i], Qnil)) + { + subprefix_stack[submenu_depth++] = prefix; + prefix = entry; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda)) + { + prefix = subprefix_stack[--submenu_depth]; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qt)) + { + prefix + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; + i += MENU_ITEMS_PANE_LENGTH; + } + /* Ignore a nil in the item list. + It's meaningful only for dialog boxes. */ + else if (EQ (XVECTOR (menu_items)->contents[i], Qquote)) + i += 1; + else + { + entry + = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE]; + if ((int) (EMACS_INT)client_data == i) + { + if (keymaps != 0) + { + int j; + + entry = Fcons (entry, Qnil); + if (!NILP (prefix)) + entry = Fcons (prefix, entry); + for (j = submenu_depth - 1; j >= 0; j--) + if (!NILP (subprefix_stack[j])) + entry = Fcons (subprefix_stack[j], entry); + } + return entry; + } + i += MENU_ITEMS_ITEM_LENGTH; + } + } +} +#endif void syms_of_menu () diff --git a/src/nsfns.m b/src/nsfns.m new file mode 100644 index 00000000000..a39a8342bf9 --- /dev/null +++ b/src/nsfns.m @@ -0,0 +1,2668 @@ +/* Functions for the NeXT/Open/GNUstep and MacOSX window system. + + Copyright (C) 1989, 1992, 1993, 1994, 2005, 2006, 2008 + Free Software Foundation, Inc.. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +Originally by Carl Edman +Updated by Christian Limpach (chris@nice.ch) +OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com) +MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net) +GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu) + +*/ + +#include <signal.h> +#include <math.h> +#include "config.h" +#include "lisp.h" +#include "blockinput.h" +#include "nsterm.h" +#include "window.h" +#include "buffer.h" +#include "keyboard.h" +#include "termhooks.h" +#include "fontset.h" + +#include "character.h" +#include "font.h" + +#if 0 +int fns_trace_num = 1; +#define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \ + __FILE__, __LINE__, ++fns_trace_num) +#else +#define NSTRACE(x) +#endif + +#ifdef HAVE_NS + +extern NSArray *ns_send_types, *ns_return_types, *ns_drag_types; + +extern Lisp_Object Qforeground_color; +extern Lisp_Object Qbackground_color; +extern Lisp_Object Qcursor_color; +extern Lisp_Object Qinternal_border_width; +extern Lisp_Object Qvisibility; +extern Lisp_Object Qcursor_type; +extern Lisp_Object Qicon_type; +extern Lisp_Object Qicon_name; +extern Lisp_Object Qicon_left; +extern Lisp_Object Qicon_top; +extern Lisp_Object Qleft; +extern Lisp_Object Qright; +extern Lisp_Object Qtop; +extern Lisp_Object Qdisplay; +extern Lisp_Object Qvertical_scroll_bars; +extern Lisp_Object Qauto_raise; +extern Lisp_Object Qauto_lower; +extern Lisp_Object Qbox; +extern Lisp_Object Qscroll_bar_width; +extern Lisp_Object Qx_resource_name; +extern Lisp_Object Qface_set_after_frame_default; +extern Lisp_Object Qunderline, Qundefined; +extern Lisp_Object Qheight, Qminibuffer, Qname, Qonly, Qwidth; +extern Lisp_Object Qunsplittable, Qmenu_bar_lines, Qbuffer_predicate, Qtitle; + +Lisp_Object Qnone; +Lisp_Object Qns_frame_parameter; +Lisp_Object Qbuffered; +Lisp_Object Qfontsize; + +/* hack for OS X file panels */ +char panelOK = 0; + +/* Alist of elements (REGEXP . IMAGE) for images of icons associated + to frames.*/ +Lisp_Object Vns_icon_type_alist; + +EmacsTooltip *ns_tooltip; + +/* Need forward declaration here to preserve organizational integrity of file */ +Lisp_Object Fns_open_connection (Lisp_Object, Lisp_Object, Lisp_Object); + +extern BOOL ns_in_resize; + + +/* ========================================================================== + + Internal utility functions + + ========================================================================== */ + + +void +check_ns (void) +{ + if (NSApp == nil) + error ("OpenStep is not in use or not initialized"); +} + + +/* Nonzero if we can use mouse menus. */ +int +have_menus_p () +{ + return NSApp != nil; +} + + +/* Extract a frame as a FRAME_PTR, defaulting to the selected frame + and checking validity for NS. */ +static FRAME_PTR +check_ns_frame (Lisp_Object frame) +{ + FRAME_PTR f; + + if (NILP (frame)) + f = SELECTED_FRAME (); + else + { + CHECK_LIVE_FRAME (frame); + f = XFRAME (frame); + } + if (! FRAME_NS_P (f)) + error ("non-NS frame used"); + return f; +} + + +/* Let the user specify an NS display with a frame. + nil stands for the selected frame--or, if that is not an NS frame, + the first NS display on the list. */ +static struct ns_display_info * +check_ns_display_info (Lisp_Object frame) +{ + if (NILP (frame)) + { + struct frame *f = SELECTED_FRAME (); + if (FRAME_NS_P (f) && FRAME_LIVE_P (f) ) + return FRAME_NS_DISPLAY_INFO (f); + else if (ns_display_list != 0) + return ns_display_list; + else + error ("NS windows are not in use or not initialized"); + } + else if (INTEGERP (frame)) + { + struct terminal *t = get_terminal (frame, 1); + + if (t->type != output_ns) + error ("Terminal %d is not an NS display", XINT (frame)); + + return t->display_info.ns; + } + else if (STRINGP (frame)) + return ns_display_info_for_name (frame); + else + { + FRAME_PTR f; + + CHECK_LIVE_FRAME (frame); + f = XFRAME (frame); + if (! FRAME_NS_P (f)) + error ("non-NS frame used"); + return FRAME_NS_DISPLAY_INFO (f); + } + return NULL; /* shut compiler up */ +} + + +static id +ns_get_window (Lisp_Object maybeFrame) +{ + id view =nil, window =nil; + + if (!FRAMEP (maybeFrame) || !FRAME_NS_P (XFRAME (maybeFrame))) + maybeFrame = selected_frame;/*wrong_type_argument (Qframep, maybeFrame); */ + + if (!NILP (maybeFrame)) + view = FRAME_NS_VIEW (XFRAME (maybeFrame)); + if (view) window =[view window]; + + return window; +} + + +static NSScreen * +ns_get_screen (Lisp_Object anythingUnderTheSun) +{ + id window =nil; + NSScreen *screen = 0; + + struct terminal *terminal; + struct ns_display_info *dpyinfo; + struct frame *f = NULL; + Lisp_Object frame; + + if (INTEGERP (anythingUnderTheSun)) { + /* we got a terminal */ + terminal = get_terminal (anythingUnderTheSun, 1); + dpyinfo = terminal->display_info.ns; + f = dpyinfo->ns_focus_frame; + if (!f) + f = dpyinfo->ns_highlight_frame; + + } else if (FRAMEP (anythingUnderTheSun) && + FRAME_NS_P (XFRAME (anythingUnderTheSun))) { + /* we got a frame */ + f = XFRAME (anythingUnderTheSun); + + } else if (STRINGP (anythingUnderTheSun)) { /* FIXME/cl for multi-display */ + } + + if (!f) + f = SELECTED_FRAME (); + if (f) + { + XSETFRAME (frame, f); + window = ns_get_window (frame); + } + + if (window) + screen = [window screen]; + if (!screen) + screen = [NSScreen mainScreen]; + + return screen; +} + + +/* Return the X display structure for the display named NAME. + Open a new connection if necessary. */ +struct ns_display_info * +ns_display_info_for_name (name) + Lisp_Object name; +{ + Lisp_Object names; + struct ns_display_info *dpyinfo; + + CHECK_STRING (name); + + for (dpyinfo = ns_display_list, names = ns_display_name_list; + dpyinfo; + dpyinfo = dpyinfo->next, names = XCDR (names)) + { + Lisp_Object tem; + tem = Fstring_equal (XCAR (XCAR (names)), name); + if (!NILP (tem)) + return dpyinfo; + } + + error ("Emacs for OpenStep does not yet support multi-display."); + + Fns_open_connection (name, Qnil, Qnil); + dpyinfo = ns_display_list; + + if (dpyinfo == 0) + error ("OpenStep on %s not responding.\n", XSTRING (name)->data); + + return dpyinfo; +} + + +static Lisp_Object +interpret_services_menu (NSMenu *menu, Lisp_Object prefix, Lisp_Object old) +/* -------------------------------------------------------------------------- + Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side + -------------------------------------------------------------------------- */ +{ + int i, count; + id<NSMenuItem> item; + const char *name; + Lisp_Object nameStr; + unsigned short key; + NSString *keys; + Lisp_Object res; + + count = [menu numberOfItems]; + for (i = 0; i<count; i++) + { + item = [menu itemAtIndex: i]; + name = [[item title] UTF8String]; + if (!name) continue; + + nameStr = build_string (name); + + if ([item hasSubmenu]) + { + old = interpret_services_menu ([item submenu], + Fcons (nameStr, prefix), old); + } + else + { + keys = [item keyEquivalent]; + if (keys && [keys length] ) + { + key = [keys characterAtIndex: 0]; + res = make_number (key|super_modifier); + } + else + { + res = Qundefined; + } + old = Fcons (Fcons (res, + Freverse (Fcons (nameStr, + prefix))), + old); + } + } + return old; +} + + + +/* ========================================================================== + + Frame parameter setters + + ========================================================================== */ + + +static void +ns_set_foreground_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + NSColor *col; + + if (ns_lisp_to_color (arg, &col)) + { + store_frame_param (f, Qforeground_color, oldval); + error ("Unknown color"); + } + + [col retain]; + [f->output_data.ns->foreground_color release]; + f->output_data.ns->foreground_color = col; + + if (FRAME_NS_VIEW (f)) + { + update_face_from_frame_parameter (f, Qforeground_color, arg); + /*recompute_basic_faces (f); */ + if (FRAME_VISIBLE_P (f)) + redraw_frame (f); + } +} + + +static void +ns_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + struct face *face; + NSColor *col; + NSView *view = FRAME_NS_VIEW (f); + float alpha; + + if (ns_lisp_to_color (arg, &col)) + { + store_frame_param (f, Qbackground_color, oldval); + error ("Unknown color"); + } + + /* clear the frame; in some instances the NS-internal GC appears not to + update, or it does update and cannot clear old text properly */ + if (FRAME_VISIBLE_P (f)) + ns_clear_frame (f); + + [col retain]; + [f->output_data.ns->background_color release]; + f->output_data.ns->background_color = col; + if (view != nil) + { + [[view window] setBackgroundColor: col]; + alpha = [col alphaComponent]; + +#ifdef NS_IMPL_COCOA + /* the alpha code below only works on 10.4, so we need to do something + else (albeit less good) otherwise. + Check NSApplication.h for useful NSAppKitVersionNumber values. */ + if (NSAppKitVersionNumber < 744.0) + [[view window] setAlphaValue: alpha]; +#endif + + if (alpha != 1.0) + [[view window] setOpaque: NO]; + else + [[view window] setOpaque: YES]; + + face = FRAME_DEFAULT_FACE (f); + if (face) + { + col = NS_FACE_BACKGROUND (face); + face->background = + (EMACS_UINT) [[col colorWithAlphaComponent: alpha] retain]; + [col release]; + + update_face_from_frame_parameter (f, Qbackground_color, arg); + } + + if (FRAME_VISIBLE_P (f)) + redraw_frame (f); + } +} + + +static void +ns_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + NSColor *col; + + if (ns_lisp_to_color (arg, &col)) + { + store_frame_param (f, Qcursor_color, oldval); + error ("Unknown color"); + } + + [f->output_data.ns->desired_cursor_color release]; + f->output_data.ns->desired_cursor_color = [col retain]; + + if (FRAME_VISIBLE_P (f)) + { + x_update_cursor (f, 0); + x_update_cursor (f, 1); + } + update_face_from_frame_parameter (f, Qcursor_color, arg); +} + + +static void +ns_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + NSView *view = FRAME_NS_VIEW (f); + NSTRACE (ns_set_icon_name); + + if (ns_in_resize) + return; + + /* see if it's changed */ + if (STRINGP (arg)) + { + if (STRINGP (oldval) && EQ (Fstring_equal (oldval, arg), Qt)) + return; + } + else if (!STRINGP (oldval) && EQ (oldval, Qnil) == EQ (arg, Qnil)) + return; + + f->icon_name = arg; + + if (NILP (arg)) + { + if (!NILP (f->title)) + arg = f->title; + else + /* explicit name and no icon-name -> explicit_name */ + if (f->explicit_name) + arg = f->name; + else + { + /* no explicit name and no icon-name -> + name has to be rebuild from icon_title_format */ + windows_or_buffers_changed++; + return; + } + } + + /* Don't change the name if it's already NAME. */ + if ([[view window] miniwindowTitle] && + ([[[view window] miniwindowTitle] + isEqualToString: [NSString stringWithUTF8String: + XSTRING (arg)->data]])) + return; + + [[view window] setMiniwindowTitle: + [NSString stringWithUTF8String: XSTRING (arg)->data]]; +} + + +static void +ns_set_name_iconic (struct frame *f, Lisp_Object name, int explicit) +{ + NSView *view = FRAME_NS_VIEW (f); + NSTRACE (ns_set_name_iconic); + + if (ns_in_resize) + return; + + /* Make sure that requests from lisp code override requests from + Emacs redisplay code. */ + if (explicit) + { + /* If we're switching from explicit to implicit, we had better + update the mode lines and thereby update the title. */ + if (f->explicit_name && NILP (name)) + update_mode_lines = 1; + + f->explicit_name = ! NILP (name); + } + else if (f->explicit_name) + name = f->name; + + /* title overrides explicit name */ + if (! NILP (f->title)) + name = f->title; + + /* icon_name overrides title and explicit name */ + if (! NILP (f->icon_name)) + name = f->icon_name; + + if (NILP (name)) + name = build_string + ([[[NSProcessInfo processInfo] processName] UTF8String]); + else + CHECK_STRING (name); + + /* Don't change the name if it's already NAME. */ + if ([[view window] miniwindowTitle] && + ([[[view window] miniwindowTitle] + isEqualToString: [NSString stringWithUTF8String: + XSTRING (name)->data]])) + return; + + [[view window] setMiniwindowTitle: + [NSString stringWithUTF8String: XSTRING (name)->data]]; +} + + +static void +ns_set_name (struct frame *f, Lisp_Object name, int explicit) +{ + NSView *view = FRAME_NS_VIEW (f); + NSTRACE (ns_set_name); + + if (ns_in_resize) + return; + + /* Make sure that requests from lisp code override requests from + Emacs redisplay code. */ + if (explicit) + { + /* If we're switching from explicit to implicit, we had better + update the mode lines and thereby update the title. */ + if (f->explicit_name && NILP (name)) + update_mode_lines = 1; + + f->explicit_name = ! NILP (name); + } + else if (f->explicit_name) + return; + + if (NILP (name)) + name = build_string + ([[[NSProcessInfo processInfo] processName] UTF8String]); + + f->name = name; + + /* title overrides explicit name */ + if (! NILP (f->title)) + name = f->title; + + CHECK_STRING (name); + + /* Don't change the name if it's already NAME. */ + if ([[[view window] title] + isEqualToString: [NSString stringWithUTF8String: + XSTRING (name)->data]]) + return; + [[view window] setTitle: [NSString stringWithUTF8String: + XSTRING (name)->data]]; +} + + +/* This function should be called when the user's lisp code has + specified a name for the frame; the name will override any set by the + redisplay code. */ +static void +ns_explicitly_set_name (FRAME_PTR f, Lisp_Object arg, Lisp_Object oldval) +{ + NSTRACE (ns_explicitly_set_name); + ns_set_name_iconic (f, arg, 1); + ns_set_name (f, arg, 1); +} + + +/* This function should be called by Emacs redisplay code to set the + name; names set this way will never override names set by the user's + lisp code. */ +void +x_implicitly_set_name (FRAME_PTR f, Lisp_Object arg, Lisp_Object oldval) +{ + NSTRACE (x_implicitly_set_name); + if (FRAME_ICONIFIED_P (f)) + ns_set_name_iconic (f, arg, 0); + else + ns_set_name (f, arg, 0); +} + + +/* Change the title of frame F to NAME. + If NAME is nil, use the frame name as the title. + + If EXPLICIT is non-zero, that indicates that lisp code is setting the + name; if NAME is a string, set F's name to NAME and set + F->explicit_name; if NAME is Qnil, then clear F->explicit_name. + + If EXPLICIT is zero, that indicates that Emacs redisplay code is + suggesting a new name, which lisp code should override; if + F->explicit_name is set, ignore the new name; otherwise, set it. */ +static void +ns_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name) +{ + NSTRACE (ns_set_title); + /* Don't change the title if it's already NAME. */ + if (EQ (name, f->title)) + return; + + update_mode_lines = 1; + + f->title = name; +} + + +void +ns_set_name_as_filename (struct frame *f) +{ + NSView *view = FRAME_NS_VIEW (f); + Lisp_Object name; + Lisp_Object buf = XWINDOW (f->selected_window)->buffer; + const char *title; + NSAutoreleasePool *pool; + NSTRACE (ns_set_name_as_filename); + + if (f->explicit_name || ! NILP (f->title) || ns_in_resize) + return; + + BLOCK_INPUT; + pool = [[NSAutoreleasePool alloc] init]; + name =XBUFFER (buf)->filename; + if (NILP (name) || FRAME_ICONIFIED_P (f)) name =XBUFFER (buf)->name; + + if (FRAME_ICONIFIED_P (f) && !NILP (f->icon_name)) + name = f->icon_name; + + if (NILP (name)) + name = build_string + ([[[NSProcessInfo processInfo] processName] UTF8String]); + else + CHECK_STRING (name); + + title = FRAME_ICONIFIED_P (f) ? [[[view window] miniwindowTitle] UTF8String] + : [[[view window] title] UTF8String]; + + if (title && (! strcmp (title, XSTRING (name)->data))) + { + [pool release]; + UNBLOCK_INPUT; + return; + } + + if (! FRAME_ICONIFIED_P (f)) + { +#ifdef NS_IMPL_COCOA + /* work around a bug observed on 10.3 where + setTitleWithRepresentedFilename does not clear out previous state + if given filename does not exist */ + NSString *str = [NSString stringWithUTF8String: XSTRING (name)->data]; + if (![[NSFileManager defaultManager] fileExistsAtPath: str]) + { + [[view window] setTitleWithRepresentedFilename: @""]; + [[view window] setTitle: str]; + } + else + { + [[view window] setTitleWithRepresentedFilename: str]; + } +#else + [[view window] setTitleWithRepresentedFilename: + [NSString stringWithUTF8String: XSTRING (name)->data]]; +#endif + f->name = name; + } + else + { + [[view window] setMiniwindowTitle: + [NSString stringWithUTF8String: XSTRING (name)->data]]; + } + [pool release]; + UNBLOCK_INPUT; +} + + +void +ns_set_doc_edited (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + NSView *view = FRAME_NS_VIEW (f); + NSAutoreleasePool *pool; + BLOCK_INPUT; + pool = [[NSAutoreleasePool alloc] init]; + [[view window] setDocumentEdited: !NILP (arg)]; + [pool release]; + UNBLOCK_INPUT; +} + + +static void +ns_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + int nlines; + int olines = FRAME_MENU_BAR_LINES (f); + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + if (INTEGERP (value)) + nlines = XINT (value); + else + nlines = 0; + + FRAME_MENU_BAR_LINES (f) = 0; + if (nlines) + { + FRAME_EXTERNAL_MENU_BAR (f) = 1; +/* does for all frames, whereas we just want for one frame + [NSMenu setMenuBarVisible: YES]; */ + } + else + { + if (FRAME_EXTERNAL_MENU_BAR (f) == 1) + free_frame_menubar (f); +/* [NSMenu setMenuBarVisible: NO]; */ + FRAME_EXTERNAL_MENU_BAR (f) = 0; + } +} + + +/* 23: PENDING: there is an erroneous direct call in window.c to this fn */ +void +x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + ns_set_menu_bar_lines (f, value, oldval); +} + + +/* 23: toolbar support */ +static void +ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + int nlines; + Lisp_Object root_window; + + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + if (INTEGERP (value) && XINT (value) >= 0) + nlines = XFASTINT (value); + else + nlines = 0; + + if (nlines) + { + FRAME_EXTERNAL_TOOL_BAR (f) = 1; + update_frame_tool_bar (f); + } + else + { + if (FRAME_EXTERNAL_TOOL_BAR (f)) + { + free_frame_tool_bar (f); + FRAME_EXTERNAL_TOOL_BAR (f) = 0; + } + } + + x_set_window_size (f, 0, f->text_cols, f->text_lines); +} + + +/* 23: PENDING: there is an erroneous direct call in window.c to this fn */ +void +x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + ns_set_tool_bar_lines (f, value, oldval); +} + + +void +ns_implicitly_set_icon_type (struct frame *f) +{ + Lisp_Object tem; + EmacsView *view = FRAME_NS_VIEW (f); + id image =nil; + Lisp_Object chain, elt; + NSAutoreleasePool *pool; + BOOL setMini = YES; + + NSTRACE (ns_implicitly_set_icon_type); + + BLOCK_INPUT; + pool = [[NSAutoreleasePool alloc] init]; + if (f->output_data.ns->miniimage + && [[NSString stringWithUTF8String: XSTRING (f->name)->data] + isEqualToString: [(NSImage *)f->output_data.ns->miniimage name]]) + { + [pool release]; + UNBLOCK_INPUT; + return; + } + + tem = assq_no_quit (Qicon_type, f->param_alist); + if (CONSP (tem) && ! NILP (XCDR (tem))) + { + [pool release]; + UNBLOCK_INPUT; + return; + } + + for (chain = Vns_icon_type_alist; + (image = nil) && CONSP (chain); + chain = XCDR (chain)) + { + elt = XCAR (chain); + /* special case: 't' means go by file type */ + if (SYMBOLP (elt) && elt == Qt && XSTRING (f->name)->data[0] == '/') + { + NSString *str = + [NSString stringWithUTF8String: XSTRING (f->name)->data]; + if ([[NSFileManager defaultManager] fileExistsAtPath: str]) + image = [[[NSWorkspace sharedWorkspace] iconForFile: str] retain]; + } + else if (CONSP (elt) && + STRINGP (XCAR (elt)) && + STRINGP (XCDR (elt)) && + fast_string_match (XCAR (elt), f->name) >= 0) + { + image = [EmacsImage allocInitFromFile: XCDR (elt)]; + if (image == nil) + image = [[NSImage imageNamed: + [NSString stringWithUTF8String: + XSTRING (XCDR (elt))->data]] retain]; + } + } + + if (image == nil) + { + image = [[[NSWorkspace sharedWorkspace] iconForFileType: @"text"] retain]; + setMini = NO; + } + + [f->output_data.ns->miniimage release]; + f->output_data.ns->miniimage = image; + [view setMiniwindowImage: setMini]; + [pool release]; + UNBLOCK_INPUT; +} + + +static void +ns_set_icon_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + EmacsView *view = FRAME_NS_VIEW (f); + id image = nil; + BOOL setMini = YES; + + NSTRACE (ns_set_icon_type); + + if (!NILP (arg) && SYMBOLP (arg)) + { + arg =build_string (XSTRING (XSYMBOL (arg)->xname)->data); + store_frame_param (f, Qicon_type, arg); + } + + /* do it the implicit way */ + if (NILP (arg)) + { + ns_implicitly_set_icon_type (f); + return; + } + + CHECK_STRING (arg); + + image = [EmacsImage allocInitFromFile: arg]; + if (image == nil) + image =[NSImage imageNamed: [NSString stringWithUTF8String: + XSTRING (arg)->data]]; + + if (image == nil) + { + image = [NSImage imageNamed: @"text"]; + setMini = NO; + } + + f->output_data.ns->miniimage = image; + [view setMiniwindowImage: setMini]; +} + + +/* 23: added Xism; we stub out (we do implement this in ns-win.el) */ +int +XParseGeometry (char *string, int *x, int *y, + unsigned int *width, unsigned int *height) +{ + message1 ("Warning: XParseGeometry not supported under NS.\n"); + return 0; +} + + +/*PENDING: move to nsterm? */ +int +ns_lisp_to_cursor_type (Lisp_Object arg) +{ + char *str; + if (XTYPE (arg) == Lisp_String) + str =XSTRING (arg)->data; + else if (XTYPE (arg) == Lisp_Symbol) + str =XSTRING (XSYMBOL (arg)->xname)->data; + else return -1; + if (!strcmp (str, "box")) return filled_box; + if (!strcmp (str, "hollow")) return hollow_box; + if (!strcmp (str, "underscore")) return underscore; + if (!strcmp (str, "bar")) return bar; + if (!strcmp (str, "no")) return no_highlight; + return -1; +} + + +Lisp_Object +ns_cursor_type_to_lisp (int arg) +{ + switch (arg) + { + case filled_box: return Qbox; + case hollow_box: return intern ("hollow"); + case underscore: return intern ("underscore"); + case bar: return intern ("bar"); + case no_highlight: + default: return intern ("no"); + } +} + + +static void +ns_set_cursor_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + int val; + + val = ns_lisp_to_cursor_type (arg); + if (val >= 0) + { + f->output_data.ns->desired_cursor =val; + } + else + { + store_frame_param (f, Qcursor_type, oldval); + error ("the `cursor-type' frame parameter should be either `no', `box', \ +`hollow', `underscore' or `bar'."); + } + + update_mode_lines++; +} + + +/* 23: called to set mouse pointer color, but all other terms use it to + initialize pointer types (and don't set the color ;) */ +static void +ns_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + /* don't think we can do this on NS */ +} + + +static void +ns_icon (struct frame *f, Lisp_Object parms) +/* -------------------------------------------------------------------------- + Strangely-named function to set icon position parameters in frame. + This is irrelevant under OS X, but might be needed under GNUstep, + depending on the window manager used. Note, this is not a standard + frame parameter-setter; it is called directly from x-create-frame. + -------------------------------------------------------------------------- */ +{ + Lisp_Object icon_x, icon_y; + struct ns_display_info *dpyinfo = check_ns_display_info (Qnil); + + f->output_data.ns->icon_top = Qnil; + f->output_data.ns->icon_left = Qnil; + + /* Set the position of the icon. */ + icon_x = x_get_arg (dpyinfo, parms, Qicon_left, 0, 0, RES_TYPE_NUMBER); + icon_y = x_get_arg (dpyinfo, parms, Qicon_top, 0, 0, RES_TYPE_NUMBER); + if (!EQ (icon_x, Qunbound) && !EQ (icon_y, Qunbound)) + { + CHECK_NUMBER (icon_x); + CHECK_NUMBER (icon_y); + f->output_data.ns->icon_top = icon_y; + f->output_data.ns->icon_left = icon_x; + } + else if (!EQ (icon_x, Qunbound) || !EQ (icon_y, Qunbound)) + error ("Both left and top icon corners of icon must be specified"); +} + + +/* 23 Note: commented out ns_... entries are no longer used in 23. + commented out x_... entries have not been implemented yet. + see frame.c for template, also where all generic OK functions are impl */ +frame_parm_handler ns_frame_parm_handlers[] = +{ + x_set_autoraise, /* generic OK */ + x_set_autolower, /* generic OK */ + ns_set_background_color, + 0, /* x_set_border_color, may be impossible under NS */ + 0, /* x_set_border_width, may be impossible under NS */ + ns_set_cursor_color, + ns_set_cursor_type, + x_set_font, /* generic OK */ + ns_set_foreground_color, + ns_set_icon_name, + ns_set_icon_type, + x_set_internal_border_width, /* generic OK */ + ns_set_menu_bar_lines, + ns_set_mouse_color, + ns_explicitly_set_name, + x_set_scroll_bar_width, /* generic OK */ + ns_set_title, + x_set_unsplittable, /* generic OK */ + x_set_vertical_scroll_bars, /* generic OK */ + x_set_visibility, /* generic OK */ + ns_set_tool_bar_lines, + 0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */ + 0, /* x_set_scroll_bar_background, will ignore (not possible on NS) */ + x_set_screen_gamma, /* generic OK */ + x_set_line_spacing, /* generic OK, sets f->extra_line_spacing to int */ + x_set_fringe_width, /* generic OK */ + x_set_fringe_width, /* generic OK */ + 0, /* x_set_wait_for_wm, will ignore */ + 0, /* x_set_fullscreen will ignore */ + x_set_font_backend /* generic OK */ +}; + + +DEFUN ("x-create-frame", Fns_create_frame, Sns_create_frame, + 1, 1, 0, + "Make a new NS window, which is called a \"frame\" in Emacs terms.\n\ +Return an Emacs frame object representing the X window.\n\ +ALIST is an alist of frame parameters.\n\ +If the parameters specify that the frame should not have a minibuffer,\n\ +and do not specify a specific minibuffer window to use,\n\ +then `default-minibuffer-frame' must be a frame whose minibuffer can\n\ +be shared by the new frame.") + (parms) + Lisp_Object parms; +{ + static int desc_ctr = 1; + struct frame *f; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + Lisp_Object frame, tem; + Lisp_Object name; + int minibuffer_only = 0; + int count = specpdl_ptr - specpdl; + Lisp_Object display; + struct ns_display_info *dpyinfo = NULL; + Lisp_Object parent; + struct kboard *kb; + Lisp_Object tfont, tfontsize; + int window_prompting = 0; + int width, height; + + check_ns (); + + display = x_get_arg (dpyinfo, parms, Qterminal, 0, 0, RES_TYPE_STRING); + if (EQ (display, Qunbound)) + display = Qnil; + dpyinfo = check_ns_display_info (display); + + if (!dpyinfo->terminal->name) + error ("Terminal is not live, can't create new frames on it"); + + kb = dpyinfo->terminal->kboard; + + name = x_get_arg (dpyinfo, parms, Qname, 0, 0, RES_TYPE_STRING); + if (!STRINGP (name) + && ! EQ (name, Qunbound) + && ! NILP (name)) + error ("Invalid frame name--not a string or nil"); + + if (STRINGP (name)) + Vx_resource_name = name; + + parent = x_get_arg (dpyinfo, parms, Qparent_id, 0, 0, RES_TYPE_NUMBER); + if (EQ (parent, Qunbound)) + parent = Qnil; + if (! NILP (parent)) + CHECK_NUMBER (parent); + + frame = Qnil; + GCPRO4 (parms, parent, name, frame); + + tem = x_get_arg (dpyinfo, parms, Qminibuffer, "minibuffer", "Minibuffer", + RES_TYPE_SYMBOL); + if (EQ (tem, Qnone) || NILP (tem)) + { + f = make_frame_without_minibuffer (Qnil, kb, display); + } + else if (EQ (tem, Qonly)) + { + f = make_minibuffer_frame (); + minibuffer_only = 1; + } + else if (WINDOWP (tem)) + { + f = make_frame_without_minibuffer (tem, kb, display); + } + else + { + f = make_frame (1); + } + + /* Set the name; the functions to which we pass f expect the name to + be set. */ + if (EQ (name, Qunbound) || NILP (name) || (XTYPE (name) != Lisp_String)) + { + f->name = + build_string ([[[NSProcessInfo processInfo] processName] UTF8String]); + f->explicit_name =0; + } + else + { + f->name = name; + f->explicit_name = 1; + specbind (Qx_resource_name, name); + } + + XSETFRAME (frame, f); + FRAME_CAN_HAVE_SCROLL_BARS (f) = 1; + + f->terminal = dpyinfo->terminal; + f->terminal->reference_count++; + + f->output_method = output_ns; + f->output_data.ns = (struct ns_output *)xmalloc (sizeof *(f->output_data.ns)); + bzero (f->output_data.ns, sizeof (*(f->output_data.ns))); + + FRAME_FONTSET (f) = -1; + + /* record_unwind_protect (unwind_create_frame, frame); safety; maybe later? */ + + f->icon_name = x_get_arg (dpyinfo, parms, Qicon_name, "iconName", "Title", + RES_TYPE_STRING); + if (EQ (f->icon_name, Qunbound) || (XTYPE (f->icon_name) != Lisp_String)) + f->icon_name = Qnil; + + FRAME_NS_DISPLAY_INFO (f) = dpyinfo; + + f->output_data.ns->window_desc = desc_ctr++; + if (!NILP (parent)) + { + f->output_data.ns->parent_desc = (Window) XFASTINT (parent); + f->output_data.ns->explicit_parent = 1; + } + else + { + f->output_data.ns->parent_desc = FRAME_NS_DISPLAY_INFO (f)->root_window; + f->output_data.ns->explicit_parent = 0; + } + + f->resx = dpyinfo->resx; + f->resy = dpyinfo->resy; + + BLOCK_INPUT; + register_font_driver (&nsfont_driver, f); + x_default_parameter (f, parms, Qfont_backend, Qnil, + "fontBackend", "FontBackend", RES_TYPE_STRING); + + { + /* use for default font name */ + id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */ + tfontsize = x_default_parameter (f, parms, Qfontsize, + make_number (0 /*(int)[font pointSize]*/), + "fontSize", "FontSize", RES_TYPE_NUMBER); + tfont = x_default_parameter (f, parms, Qfont, + build_string ([[font fontName] UTF8String]), + "font", "Font", RES_TYPE_STRING); + } + UNBLOCK_INPUT; + + x_default_parameter (f, parms, Qborder_width, make_number (0), + "borderwidth", "BorderWidth", RES_TYPE_NUMBER); + x_default_parameter (f, parms, Qinternal_border_width, make_number (2), + "internalBorderWidth", "InternalBorderWidth", + RES_TYPE_NUMBER); + + /* default scrollbars on right on Mac */ + { + Lisp_Object spos = +#ifdef NS_IMPL_GNUSTEP + Qt; +#else + Qright; +#endif + x_default_parameter (f, parms, Qvertical_scroll_bars, spos, + "verticalScrollBars", "VerticalScrollBars", + RES_TYPE_SYMBOL); + } + x_default_parameter (f, parms, Qforeground_color, build_string ("Black"), + "foreground", "Foreground", RES_TYPE_STRING); + x_default_parameter (f, parms, Qbackground_color, build_string ("White"), + "background", "Background", RES_TYPE_STRING); + x_default_parameter (f, parms, Qcursor_color, build_string ("grey"), + "cursorColor", "CursorColor", RES_TYPE_STRING); + /*PENDING: not suppported yet in NS */ + x_default_parameter (f, parms, Qline_spacing, Qnil, + "lineSpacing", "LineSpacing", RES_TYPE_NUMBER); + x_default_parameter (f, parms, Qleft_fringe, Qnil, + "leftFringe", "LeftFringe", RES_TYPE_NUMBER); + x_default_parameter (f, parms, Qright_fringe, Qnil, + "rightFringe", "RightFringe", RES_TYPE_NUMBER); + /* end PENDING */ + + init_frame_faces (f); + + x_default_parameter (f, parms, Qmenu_bar_lines, make_number (0), "menuBar", + "menuBar", RES_TYPE_NUMBER); + x_default_parameter (f, parms, Qtool_bar_lines, make_number (0), "toolBar", + "toolBar", RES_TYPE_NUMBER); + x_default_parameter (f, parms, Qbuffer_predicate, Qnil, "bufferPredicate", + "BufferPredicate", RES_TYPE_SYMBOL); + x_default_parameter (f, parms, Qtitle, Qnil, "title", "Title", + RES_TYPE_STRING); + +/*PENDING: other terms seem to get away w/o this complexity.. */ + if (NILP (Fassq (Qwidth, parms))) + { + Lisp_Object value = + x_get_arg (dpyinfo, parms, Qwidth, "width", "Width", RES_TYPE_NUMBER); + if (! EQ (value, Qunbound)) + parms = Fcons (Fcons (Qwidth, value), parms); + } + if (NILP (Fassq (Qheight, parms))) + { + Lisp_Object value = + x_get_arg (dpyinfo, parms, Qheight, "height", "Height", + RES_TYPE_NUMBER); + if (! EQ (value, Qunbound)) + parms = Fcons (Fcons (Qheight, value), parms); + } + if (NILP (Fassq (Qleft, parms))) + { + Lisp_Object value = + x_get_arg (dpyinfo, parms, Qleft, "left", "Left", RES_TYPE_NUMBER); + if (! EQ (value, Qunbound)) + parms = Fcons (Fcons (Qleft, value), parms); + } + if (NILP (Fassq (Qtop, parms))) + { + Lisp_Object value = + x_get_arg (dpyinfo, parms, Qtop, "top", "Top", RES_TYPE_NUMBER); + if (! EQ (value, Qunbound)) + parms = Fcons (Fcons (Qtop, value), parms); + } + + window_prompting = x_figure_window_size (f, parms, 1); + + tem = x_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, RES_TYPE_BOOLEAN); + f->no_split = minibuffer_only || (!EQ (tem, Qunbound) && !EQ (tem, Qnil)); + + /* NOTE: on other terms, this is done in set_mouse_color, however this + was not getting called under NS */ + f->output_data.ns->text_cursor = [NSCursor IBeamCursor]; + f->output_data.ns->nontext_cursor = [NSCursor arrowCursor]; + f->output_data.ns->modeline_cursor = [NSCursor pointingHandCursor]; + f->output_data.ns->hand_cursor = [NSCursor pointingHandCursor]; + f->output_data.ns->hourglass_cursor = [NSCursor disappearingItemCursor]; + f->output_data.ns->horizontal_drag_cursor = [NSCursor resizeLeftRightCursor]; + FRAME_NS_DISPLAY_INFO (f)->vertical_scroll_bar_cursor = + [NSCursor arrowCursor]; + f->output_data.ns->current_pointer = f->output_data.ns->text_cursor; + + [[EmacsView alloc] initFrameFromEmacs: f]; + + ns_icon (f, parms); + + /* It is now ok to make the frame official even if we get an error below. + The frame needs to be on Vframe_list or making it visible won't work. */ + Vframe_list = Fcons (frame, Vframe_list); + /*FRAME_NS_DISPLAY_INFO (f)->reference_count++; */ + + x_default_parameter (f, parms, Qcursor_type, Qbox, "cursorType", "CursorType", + RES_TYPE_SYMBOL); + x_default_parameter (f, parms, Qscroll_bar_width, Qnil, "scrollBarWidth", + "ScrollBarWidth", RES_TYPE_NUMBER); + x_default_parameter (f, parms, Qicon_type, Qnil, "bitmapIcon", "BitmapIcon", + RES_TYPE_SYMBOL); + x_default_parameter (f, parms, Qauto_raise, Qnil, "autoRaise", "AutoRaise", + RES_TYPE_BOOLEAN); + x_default_parameter (f, parms, Qauto_lower, Qnil, "autoLower", "AutoLower", + RES_TYPE_BOOLEAN); + x_default_parameter (f, parms, Qbuffered, Qt, "buffered", "Buffered", + RES_TYPE_BOOLEAN); + + width = FRAME_COLS (f); + height = FRAME_LINES (f); + + SET_FRAME_COLS (f, 0); + FRAME_LINES (f) = 0; + change_frame_size (f, height, width, 1, 0, 0); + + if (! f->output_data.ns->explicit_parent) + { + tem = x_get_arg (dpyinfo, parms, Qvisibility, 0, 0, RES_TYPE_BOOLEAN); + if (EQ (tem, Qunbound)) + tem = Qnil; + + x_set_visibility (f, tem, Qnil); + if (EQ (tem, Qt)) + [[FRAME_NS_VIEW (f) window] makeKeyWindow]; + } + + if (FRAME_HAS_MINIBUF_P (f) + && (!FRAMEP (kb->Vdefault_minibuffer_frame) + || !FRAME_LIVE_P (XFRAME (kb->Vdefault_minibuffer_frame)))) + kb->Vdefault_minibuffer_frame = frame; + + /* All remaining specified parameters, which have not been "used" + by x_get_arg and friends, now go in the misc. alist of the frame. */ + for (tem = parms; CONSP (tem); tem = XCDR (tem)) + if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem)))) + f->param_alist = Fcons (XCAR (tem), f->param_alist); + + UNGCPRO; + Vwindow_list = Qnil; + + return unbind_to (count, frame); +} + + +/* ========================================================================== + + Lisp definitions + + ========================================================================== */ + +DEFUN ("ns-focus-frame", Fns_focus_frame, Sns_focus_frame, 1, 1, 0, + doc: /* Set the input focus to FRAME. +FRAME nil means use the selected frame. */) + (frame) + Lisp_Object frame; +{ + struct frame *f = check_ns_frame (frame); + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (f); + + if (dpyinfo->ns_focus_frame != f) + { + EmacsView *view = FRAME_NS_VIEW (f); + BLOCK_INPUT; + [[view window] makeKeyAndOrderFront: view]; + UNBLOCK_INPUT; + } + + return Qnil; +} + + +DEFUN ("ns-popup-prefs-panel", Fns_popup_prefs_panel, Sns_popup_prefs_panel, + 0, 0, "", "Pop up the preferences panel.") + () +{ + check_ns (); + [(EmacsApp *)NSApp showPreferencesWindow: NSApp]; + return Qnil; +} + + +DEFUN ("ns-popup-font-panel", Fns_popup_font_panel, Sns_popup_font_panel, + 0, 1, "", "Pop up the font panel.") + (frame) + Lisp_Object frame; +{ + id fm; + struct frame *f; + + check_ns (); + fm = [NSFontManager new]; + if (NILP (frame)) + f = SELECTED_FRAME (); + else + { + CHECK_FRAME (frame); + f = XFRAME (frame); + } + + [fm setSelectedFont: ((struct nsfont_info *)f->output_data.ns->font)->nsfont + isMultiple: NO]; + [fm orderFrontFontPanel: NSApp]; + return Qnil; +} + + +DEFUN ("ns-popup-color-panel", Fns_popup_color_panel, Sns_popup_color_panel, + 0, 1, "", "Pop up the color panel.") + (frame) + Lisp_Object frame; +{ + struct frame *f; + + check_ns (); + if (NILP (frame)) + f = SELECTED_FRAME (); + else + { + CHECK_FRAME (frame); + f = XFRAME (frame); + } + + [NSApp orderFrontColorPanel: NSApp]; + return Qnil; +} + + +DEFUN ("ns-read-file-name", Fns_read_file_name, Sns_read_file_name, 1, 4, 0, + "As read-file-name except that NS panels are used for querying, and\n\ +args are slightly different. Nil returned if no selection made.\n\ +Set ISLOAD non-nil if file being read for a save.") + (prompt, dir, isLoad, init) + Lisp_Object prompt, dir, isLoad, init; +{ + static id fileDelegate = nil; + int ret; + id panel; + NSString *fname; + + NSString *promptS = NILP (prompt) || !STRINGP (prompt) ? nil : + [NSString stringWithUTF8String: XSTRING (prompt)->data]; + NSString *dirS = NILP (dir) || !STRINGP (dir) ? + [NSString stringWithUTF8String: XSTRING (current_buffer->directory)->data] : + [NSString stringWithUTF8String: XSTRING (dir)->data]; + NSString *initS = NILP (init) || !STRINGP (init) ? nil : + [NSString stringWithUTF8String: XSTRING (init)->data]; + + check_ns (); + + if (fileDelegate == nil) + fileDelegate = [EmacsFileDelegate new]; + + [NSCursor setHiddenUntilMouseMoves: NO]; + + if ([dirS characterAtIndex: 0] == '~') + dirS = [dirS stringByExpandingTildeInPath]; + + panel = NILP (isLoad) ? + [EmacsSavePanel savePanel] : [EmacsOpenPanel openPanel]; + + [panel setTitle: promptS]; + + /* Puma (10.1) does not have */ + if ([panel respondsToSelector: @selector (setAllowsOtherFileTypes:)]) + [panel setAllowsOtherFileTypes: YES]; + + [panel setTreatsFilePackagesAsDirectories: YES]; + [panel setDelegate: fileDelegate]; + + panelOK = 0; + if (NILP (isLoad)) + { + ret = [panel runModalForDirectory: dirS file: initS]; + } + else + { + [panel setCanChooseDirectories: YES]; + ret = [panel runModalForDirectory: dirS file: initS types: nil]; + } + + ret = (ret = NSOKButton) || panelOK; + + fname = [panel filename]; + + [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow]; + + return ret ? build_string ([fname UTF8String]) : Qnil; +} + + +DEFUN ("ns-get-resource", Fns_get_resource, Sns_get_resource, 2, 2, 0, + "Return the value of the property NAME of OWNER from the defaults database.\n\ +If OWNER is nil, Emacs is assumed.") + (owner, name) + Lisp_Object owner, name; +{ + const char *value; + + check_ns (); + if (NILP (owner)) + owner = build_string + ([[[NSProcessInfo processInfo] processName] UTF8String]); + /* CHECK_STRING (owner); this should be just "Emacs" */ + CHECK_STRING (name); +/*fprintf (stderr, "ns-get-resource checking resource '%s'\n", SDATA (name)); */ + + value =[[[NSUserDefaults standardUserDefaults] + objectForKey: [NSString stringWithUTF8String: XSTRING (name)->data]] + UTF8String]; + + if (value) + return build_string (value); +/*fprintf (stderr, "Nothing found for NS resource '%s'.\n", XSTRING (name)->data); */ + return Qnil; +} + + +DEFUN ("ns-set-resource", Fns_set_resource, Sns_set_resource, 3, 3, 0, + "Set property NAME of OWNER to VALUE, from the defaults database.\n\ +If OWNER is nil, Emacs is assumed.\n\ +If VALUE is nil, the default is removed.") + (owner, name, value) + Lisp_Object owner, name, value; +{ + check_ns (); + if (NILP (owner)) + owner = + build_string ([[[NSProcessInfo processInfo] processName] UTF8String]); + CHECK_STRING (owner); + CHECK_STRING (name); + if (NILP (value)) + { + [[NSUserDefaults standardUserDefaults] removeObjectForKey: + [NSString stringWithUTF8String: XSTRING (name)->data]]; + } + else + { + CHECK_STRING (value); + [[NSUserDefaults standardUserDefaults] setObject: + [NSString stringWithUTF8String: XSTRING (value)->data] + forKey: [NSString stringWithUTF8String: + XSTRING (name)->data]]; + } + + return Qnil; +} + + +DEFUN ("ns-set-alpha", Fns_set_alpha, Sns_set_alpha, 2, 2, 0, + "Return a color same as given with alpha set to given value\n\ +from 0 to 1, where 1 is fully opaque.") + (color, alpha) + Lisp_Object color; + Lisp_Object alpha; +{ + NSColor *col; + float a; + + CHECK_STRING (color); + CHECK_NUMBER_OR_FLOAT (alpha); + + if (ns_lisp_to_color (color, &col)) + error ("Unknown color."); + + a = XFLOATINT (alpha); + if (a < 0.0 || a > 1.0) + error ("Alpha value should be between 0 and 1 inclusive."); + + col = [col colorWithAlphaComponent: a]; + return ns_color_to_lisp (col); +} + + +DEFUN ("ns-server-max-request-size", Fns_server_max_request_size, + Sns_server_max_request_size, + 0, 1, 0, + "This function is only present for completeness. It does not return\n\ +a usable result for NS windows.") + (display) + Lisp_Object display; +{ + check_ns (); + /* This function has no real equivalent under NeXTstep. Return nil to + indicate this. */ + return Qnil; +} + + +DEFUN ("ns-server-vendor", Fns_server_vendor, Sns_server_vendor, 0, 1, 0, + "Returns the vendor ID string of the NS server of display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame or a display name (a string).\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); +#ifdef NS_IMPL_GNUSTEP + return build_string ("GNU"); +#else + return build_string ("Apple"); +#endif +} + + +DEFUN ("ns-server-version", Fns_server_version, Sns_server_version, 0, 1, 0, + "Returns the version number of the NS release of display DISPLAY.\n\ +See also the function `ns-server-vendor'.\n\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame or a display name (a string).\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + /*PENDING: return GUI version on GNUSTEP, ?? on OS X */ + return build_string ("1.0"); +} + + +DEFUN ("ns-display-screens", Fns_display_screens, Sns_display_screens, 0, 1, 0, + "Returns the number of screens on the NS server of display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + int num; + + check_ns (); + num = [[NSScreen screens] count]; + + return (num != 0) ? make_number (num) : Qnil; +} + + +DEFUN ("ns-display-mm-height", Fns_display_mm_height, Sns_display_mm_height, + 0, 1, 0, + "Returns the height in millimeters of the NS display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); + return make_number ((int) + ([ns_get_screen (display) frame].size.height/(92.0/25.4))); +} + + +DEFUN ("ns-display-mm-width", Fns_display_mm_width, Sns_display_mm_width, + 0, 1, 0, + "Returns the width in millimeters of the NS display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); + return make_number ((int) + ([ns_get_screen (display) frame].size.width/(92.0/25.4))); +} + + +DEFUN ("ns-display-backing-store", Fns_display_backing_store, + Sns_display_backing_store, 0, 1, 0, + "Returns an indication of whether NS display DISPLAY does backing store.\n\ +The value may be `buffered', `retained', or `non-retained'.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.\n\ +Under NS, this may differ for each frame.") + (display) + Lisp_Object display; +{ + check_ns (); + switch ([ns_get_window (display) backingType]) + { + case NSBackingStoreBuffered: + return intern ("buffered"); + case NSBackingStoreRetained: + return intern ("retained"); + case NSBackingStoreNonretained: + return intern ("non-retained"); + default: + error ("Strange value for backingType parameter of frame"); + } + return Qnil; /* not reached, shut compiler up */ +} + + +DEFUN ("ns-display-visual-class", Fns_display_visual_class, + Sns_display_visual_class, 0, 1, 0, + "Returns the visual class of the NS display DISPLAY.\n\ +The value is one of the symbols `static-gray', `gray-scale',\n\ +`static-color', `pseudo-color', `true-color', or `direct-color'.\n\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + NSWindowDepth depth; + check_ns (); + depth = [ns_get_screen (display) depth]; + + if ( depth == NSBestDepth (NSCalibratedWhiteColorSpace, 2, 2, YES, NULL)) + return intern ("static-gray"); + else if (depth == NSBestDepth (NSCalibratedWhiteColorSpace, 8, 8, YES, NULL)) + return intern ("gray-scale"); + else if ( depth == NSBestDepth (NSCalibratedRGBColorSpace, 8, 8, YES, NULL)) + return intern ("pseudo-color"); + else if ( depth == NSBestDepth (NSCalibratedRGBColorSpace, 4, 12, NO, NULL)) + return intern ("true-color"); + else if ( depth == NSBestDepth (NSCalibratedRGBColorSpace, 8, 24, NO, NULL)) + return intern ("direct-color"); + else + /* color mgmt as far as we do it is really handled by NS itself anyway */ + return intern ("direct-color"); +} + + +DEFUN ("ns-display-save-under", Fns_display_save_under, + Sns_display_save_under, 0, 1, 0, + "Returns t if the NS display DISPLAY supports the save-under feature.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.\n\ +Under NS, this may differ for each frame.") + (display) + Lisp_Object display; +{ + check_ns (); + switch ([ns_get_window (display) backingType]) + { + case NSBackingStoreBuffered: + return Qt; + + case NSBackingStoreRetained: + case NSBackingStoreNonretained: + return Qnil; + + default: + error ("Strange value for backingType parameter of frame"); + } + return Qnil; /* not reached, shut compiler up */ +} + + +DEFUN ("ns-open-connection", Fns_open_connection, Sns_open_connection, + 1, 3, 0, "Open a connection to a NS server.\n\ +DISPLAY is the name of the display to connect to.\n\ +Optional arguments XRM-STRING and MUST-SUCCEED are currently ignored.") + (display, resource_string, must_succeed) + Lisp_Object display, resource_string, must_succeed; +{ + struct ns_display_info *dpyinfo; + + CHECK_STRING (display); + + nxatoms_of_nsselect (); + dpyinfo = ns_term_init (display); + if (dpyinfo == 0) + { + if (!NILP (must_succeed)) + fatal ("OpenStep on %s not responding.\n", + XSTRING (display)->data); + else + error ("OpenStep on %s not responding.\n", + XSTRING (display)->data); + } + + /* Register our external input/output types, used for determining + applicable services and also drag/drop eligibility. */ + ns_send_types = [[NSArray arrayWithObject: NSStringPboardType] retain]; + ns_return_types = [[NSArray arrayWithObject: NSStringPboardType] retain]; + ns_drag_types = [[NSArray arrayWithObjects: + NSStringPboardType, + NSTabularTextPboardType, + NSFilenamesPboardType, + NSURLPboardType, + NSColorPboardType, + NSFontPboardType, nil] retain]; + + return Qnil; +} + + +DEFUN ("ns-close-connection", Fns_close_connection, Sns_close_connection, + 1, 1, 0, "Close the connection to the current NS server.\n\ +The second argument DISPLAY is currently ignored, but nil would stand for\n\ +the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); +#ifdef NS_IMPL_COCOA + PSFlush (); +#endif + /*ns_delete_terminal (dpyinfo->terminal); */ + [NSApp terminate: NSApp]; + return Qnil; +} + + +DEFUN ("ns-display-list", Fns_display_list, Sns_display_list, 0, 0, 0, + "Return the list of display names that Emacs has connections to.") + () +{ + Lisp_Object tail, result; + + result = Qnil; + for (tail = ns_display_name_list; CONSP (tail); tail = XCDR (tail)) + result = Fcons (XCAR (XCAR (tail)), result); + + return result; +} + + +DEFUN ("ns-hide-others", Fns_hide_others, Sns_hide_others, + 0, 0, 0, "Hides all applications other than emacs.") + () +{ + check_ns (); + [NSApp hideOtherApplications: NSApp]; + return Qnil; +} + +DEFUN ("ns-hide-emacs", Fns_hide_emacs, Sns_hide_emacs, + 1, 1, 0, "If ON is non-nil, the entire emacs application is hidden.\n\ +Otherwise if emacs is hidden, it is unhidden.\n\ +If ON is equal to 'activate, emacs is unhidden and becomes\n\ +the active application.") + (on) + Lisp_Object on; +{ + check_ns (); + if (EQ (on, intern ("activate"))) + { + [NSApp unhide: NSApp]; + [NSApp activateIgnoringOtherApps: YES]; + } + else if (NILP (on)) + [NSApp unhide: NSApp]; + else + [NSApp hide: NSApp]; + return Qnil; +} + + +DEFUN ("ns-emacs-info-panel", Fns_emacs_info_panel, Sns_emacs_info_panel, + 0, 0, 0, "Shows the 'Info' or 'About' panel for Emacs.") + () +{ + check_ns (); + [NSApp orderFrontStandardAboutPanel: nil]; + return Qnil; +} + + +DEFUN ("x-list-fonts", Fns_list_fonts, Sns_list_fonts, 1, 4, 0, + "Return a list of the names of available fonts matching PATTERN.\n\ +If optional arguments FACE and FRAME are specified, return only fonts\n\ +the same size as FACE on FRAME.\n\ +If optional argument MAX is specified, return at most MAX matches.\n\ +\n\ +PATTERN is a regular expression; FACE is a face name - a symbol.\n\ +\n\ +The return value is a list of strings, suitable as arguments to\n\ +set-face-font.\n\ +\n\ +The font names are _NOT_ X names.") + (pattern, face, frame, max) + Lisp_Object pattern, face, frame, max; +{ + Lisp_Object flist, olist = Qnil, tem; + struct frame *f; + int maxnames; + + /* We can't simply call check_x_frame because this function may be + called before any frame is created. */ + if (NILP (frame)) + f = SELECTED_FRAME (); + else + { + CHECK_LIVE_FRAME (frame); + f = XFRAME (frame); + } + if (! FRAME_WINDOW_P (f)) + { + /* Perhaps we have not yet created any frame. */ + f = NULL; + } + + if (NILP (max)) + maxnames = 4; + else + { + CHECK_NATNUM (max); + maxnames = XFASTINT (max); + } + + /* get XLFD names */ + flist = ns_list_fonts (f, pattern, 0, maxnames); + + /* convert list into regular names */ + for (tem = flist; CONSP (tem); tem = XCDR (tem)) + { + Lisp_Object fname = XCAR (tem); + olist = Fcons (build_string (ns_xlfd_to_fontname (XSTRING (fname)->data)), + olist); + } + + return olist; +} + + +DEFUN ("ns-font-name", Fns_font_name, Sns_font_name, 1, 1, 0, + "Determine font postscript or family name from a font name string or\n\ +XLFD string. If string contains fontset' and not 'fontset-startup' it is\n\ +left alone.") + (name) + Lisp_Object name; +{ + char *nm; + CHECK_STRING (name); + nm = SDATA (name); + + if (nm[0] != '-') + return name; + if (strstr (nm, "fontset") && !strstr (nm, "fontset-startup")) + return name; + + return build_string (ns_xlfd_to_fontname (SDATA (name))); +} + + +DEFUN ("ns-list-colors", Fns_list_colors, Sns_list_colors, 0, 1, 0, + "Return a list of all available colors.\n\ +The optional argument FRAME is currently ignored.") + (frame) + Lisp_Object frame; +{ + Lisp_Object list = Qnil; + NSEnumerator *colorlists; + NSColorList *clist; + + if (!NILP (frame)) + { + CHECK_FRAME (frame); + if (! FRAME_NS_P (XFRAME (frame))) + error ("non-NS frame used in `ns-list-colors'"); + } + + BLOCK_INPUT; + + colorlists = [[NSColorList availableColorLists] objectEnumerator]; + while (clist = [colorlists nextObject]) + { + if ([[clist name] length] < 7 || + [[clist name] rangeOfString: @"PANTONE"].location == 0) + { + NSEnumerator *cnames = [[clist allKeys] reverseObjectEnumerator]; + NSString *cname; + while (cname = [cnames nextObject]) + list = Fcons (build_string ([cname UTF8String]), list); +/* for (i = [[clist allKeys] count] - 1; i >= 0; i--) + list = Fcons (build_string ([[[clist allKeys] objectAtIndex: i] + UTF8String]), list); */ + } + } + + UNBLOCK_INPUT; + + return list; +} + + +DEFUN ("ns-list-services", Fns_list_services, Sns_list_services, 0, 0, 0, + "List NS services by querying NSApp.") + () +{ + Lisp_Object ret = Qnil; + NSMenu *svcs; + id delegate; + + check_ns (); + svcs = [[NSMenu alloc] initWithTitle: @"Services"]; + [NSApp setServicesMenu: svcs]; /* this and next rebuild on <10.4 */ + [NSApp registerServicesMenuSendTypes: ns_send_types + returnTypes: ns_return_types]; + +/* On Tiger, services menu updating was made lazier (waits for user to + actually click on the menu), so we have to force things along: */ +#ifdef NS_IMPL_COCOA + if (NSAppKitVersionNumber >= 744.0) + { + delegate = [svcs delegate]; + if (delegate != nil) + { + if ([delegate respondsToSelector: @selector (menuNeedsUpdate:)]) + [delegate menuNeedsUpdate: svcs]; + if ([delegate respondsToSelector: + @selector (menu:updateItem:atIndex:shouldCancel:)]) + { + int i, len = [delegate numberOfItemsInMenu: svcs]; + for (i =0; i<len; i++) + [svcs addItemWithTitle: @"" action: NULL keyEquivalent: @""]; + for (i =0; i<len; i++) + if (![delegate menu: svcs + updateItem: (NSMenuItem *)[svcs itemAtIndex: i] + atIndex: i shouldCancel: NO]) + break; + } + } + } +#endif + + [svcs setAutoenablesItems: NO]; +#ifdef NS_IMPL_COCOA + [svcs update]; /* on OS X, converts from '/' structure */ +#endif + + ret = interpret_services_menu (svcs, Qnil, ret); + return ret; +} + + +DEFUN ("ns-perform-service", Fns_perform_service, Sns_perform_service, + 2, 2, 0, "Perform NS SERVICE on SEND which is either a string or nil.\n\ +Returns result of service as string or nil if no result.") + (service, send) + Lisp_Object service, send; +{ + id pb; + NSString *svcName; + char *utfStr; + int len; + + CHECK_STRING (service); + check_ns (); + + utfStr = XSTRING (service)->data; + svcName = [NSString stringWithUTF8String: utfStr]; + + pb =[NSPasteboard pasteboardWithUniqueName]; + ns_string_to_pasteboard (pb, send); + + if (NSPerformService (svcName, pb) == NO) + Fsignal (Qquit, Fcons (build_string ("service not available"), Qnil)); + + if ([[pb types] count] == 0) + return build_string (""); + return ns_string_from_pasteboard (pb); +} + + +DEFUN ("ns-convert-utf8-nfd-to-nfc", Fns_convert_utf8_nfd_to_nfc, + Sns_convert_utf8_nfd_to_nfc, 1, 1, 0, + "Composes character sequences in UTF-8 normal form NFD string STR to produce a normal (composed normal form NFC) string.") + (str) + Lisp_Object str; +{ + NSString *utfStr; + + CHECK_STRING (str); + utfStr = [[NSString stringWithUTF8String: XSTRING (str)->data] + precomposedStringWithCanonicalMapping]; + return build_string ([utfStr UTF8String]); +} + + +/* ========================================================================== + + Miscellaneous functions not called through hooks + + ========================================================================== */ + + +/* 23: call in image.c */ +FRAME_PTR +check_x_frame (Lisp_Object frame) +{ + return check_ns_frame (frame); +} + +/* 23: added, due to call in frame.c */ +struct ns_display_info * +check_x_display_info (Lisp_Object frame) +{ + return check_ns_display_info (frame); +} + + +/* 23: new function; we don't have much in the way of flexibility though */ +void +x_set_scroll_bar_default_width (f) + struct frame *f; +{ + int wid = FRAME_COLUMN_WIDTH (f); + FRAME_CONFIG_SCROLL_BAR_WIDTH (f) = NS_SCROLL_BAR_WIDTH_DEFAULT; + FRAME_CONFIG_SCROLL_BAR_COLS (f) = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + + wid - 1) / wid; +} + + +/* 23: terms now impl this instead of x-get-resource directly */ +const char * +x_get_string_resource (XrmDatabase rdb, char *name, char *class) +{ + /* remove appname prefix; PENDING: allow for !="Emacs" */ + char *toCheck = class + (!strncmp (class, "Emacs.", 6) ? 6 : 0); + const char *res; + check_ns (); + + /* Support emacs-20-style face resources for backwards compatibility */ + if (!strncmp (toCheck, "Face", 4)) + toCheck = name + (!strncmp (name, "emacs.", 6) ? 6 : 0); + +/*fprintf (stderr, "Checking '%s'\n", toCheck); */ + + res = [[[NSUserDefaults standardUserDefaults] objectForKey: + [NSString stringWithUTF8String: toCheck]] UTF8String]; + return !res ? NULL : + (!strncasecmp (res, "YES", 3) ? "true" : + (!strncasecmp (res, "NO", 2) ? "false" : res)); +} + + +Lisp_Object +x_get_focus_frame (struct frame *frame) +{ + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (frame); + Lisp_Object nsfocus; + + if (!dpyinfo->ns_focus_frame) + return Qnil; + + XSETFRAME (nsfocus, dpyinfo->ns_focus_frame); + return nsfocus; +} + + +int +x_pixel_width (struct frame *f) +{ + return FRAME_PIXEL_WIDTH (f); +} + + +int +x_pixel_height (struct frame *f) +{ + return FRAME_PIXEL_HEIGHT (f); +} + + +int +x_char_width (struct frame *f) +{ + return FRAME_COLUMN_WIDTH (f); +} + + +int +x_char_height (struct frame *f) +{ + return FRAME_LINE_HEIGHT (f); +} + + +int +x_screen_planes (struct frame *f) +{ + return FRAME_NS_DISPLAY_INFO (f)->n_planes; +} + + +void +x_sync (Lisp_Object frame) +{ + /* XXX Not implemented XXX */ + return; +} + + + +/* ========================================================================== + + Lisp definitions that, for whatever reason, we can't alias as 'ns-XXX'. + + ========================================================================== */ + + +DEFUN ("xw-color-defined-p", Fns_color_defined_p, Sns_color_defined_p, 1, 2, 0, + "Return t if the current NS display supports the color named COLOR.\n\ +The optional argument FRAME is currently ignored.") + (color, frame) + Lisp_Object color, frame; +{ + NSColor * col; + check_ns (); + return ns_lisp_to_color (color, &col) ? Qnil : Qt; +} + + +DEFUN ("xw-color-values", Fns_color_values, Sns_color_values, 1, 2, 0, + "Return a description of the color named COLOR.\n\ +The value is a list of integer RGBA values--(RED GREEN BLUE ALPHA).\n\ +These values appear to range from 0 to 65280; white is (65280 65280 65280 0).\n\ +The optional argument FRAME is currently ignored.") + (color, frame) + Lisp_Object color, frame; +{ + NSColor * col; + float red, green, blue, alpha; + Lisp_Object rgba[4]; + + check_ns (); + CHECK_STRING (color); + + if (ns_lisp_to_color (color, &col)) + return Qnil; + + [[col colorUsingColorSpaceName: NSCalibratedRGBColorSpace] + getRed: &red green: &green blue: &blue alpha: &alpha]; + rgba[0] = make_number (lrint (red*65280)); + rgba[1] = make_number (lrint (green*65280)); + rgba[2] = make_number (lrint (blue*65280)); + rgba[3] = make_number (lrint (alpha*65280)); + + return Flist (4, rgba); +} + + +DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0, + "Return t if the NS display supports color.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + NSWindowDepth depth; + NSString *colorSpace; + check_ns (); + depth = [ns_get_screen (display) depth]; + colorSpace = NSColorSpaceFromDepth (depth); + + return [colorSpace isEqualToString: NSDeviceWhiteColorSpace] + || [colorSpace isEqualToString: NSCalibratedWhiteColorSpace] + ? Qnil : Qt; +} + + +DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, + Sx_display_grayscale_p, 0, 1, 0, + "Return t if the NS display supports shades of gray.\n\ +Note that color displays do support shades of gray.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + NSWindowDepth depth; + check_ns (); + depth = [ns_get_screen (display) depth]; + + return NSBitsPerPixelFromDepth (depth) > 1 ? Qt : Qnil; +} + + +DEFUN ("x-display-pixel-width", Fns_display_pixel_width, Sns_display_pixel_width, + 0, 1, 0, + "Returns the width in pixels of the NS display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); + return make_number ((int) [ns_get_screen (display) frame].size.width); +} + + +DEFUN ("x-display-pixel-height", Fns_display_pixel_height, + Sns_display_pixel_height, 0, 1, 0, + "Returns the height in pixels of the NS display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); + return make_number ((int) [ns_get_screen (display) frame].size.height); +} + +DEFUN ("display-usable-bounds", Fns_display_usable_bounds, + Sns_display_usable_bounds, 0, 1, 0, + "Returns a list of integers in form (left top width height) describing the \ +usable screen area excluding reserved areas such as the Mac menu and doc, or \ +the Windows task bar.\n \ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + int top; + NSRect vScreen; + + check_ns (); + vScreen = [ns_get_screen (display) visibleFrame]; + top = vScreen.origin.y == 0.0 ? + (int) [ns_get_screen (display) frame].size.height - vScreen.size.height : 0; + + return list4 (make_number ((int) vScreen.origin.x), + make_number (top), + make_number ((int) vScreen.size.width), + make_number ((int) vScreen.size.height)); +} + + +DEFUN ("x-display-planes", Fx_display_planes, Sns_display_planes, + 0, 1, 0, + "Returns the number of bitplanes of the NS display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); + return make_number + (NSBitsPerSampleFromDepth ([ns_get_screen (display) depth])); +} + + +DEFUN ("x-display-color-cells", Fns_display_color_cells, + Sns_display_color_cells, 0, 1, 0, + "Returns the number of color cells of the NS display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame, a display name (a string), or terminal ID.\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + check_ns (); + struct ns_display_info *dpyinfo = check_ns_display_info (display); + + /* We force 24+ bit depths to 24-bit to prevent an overflow. */ + return make_number (1 << min (dpyinfo->n_planes, 24)); +} + + +/* Unused dummy def needed for compatibility. */ +Lisp_Object tip_frame; + +/*PENDING: move to xdisp or similar */ +static void +compute_tip_xy (f, parms, dx, dy, width, height, root_x, root_y) + struct frame *f; + Lisp_Object parms, dx, dy; + int width, height; + int *root_x, *root_y; +{ + Lisp_Object left, top; + EmacsView *view = FRAME_NS_VIEW (f); + NSPoint pt; + + /* Start with user-specified or mouse position. */ + left = Fcdr (Fassq (Qleft, parms)); + if (INTEGERP (left)) + pt.x = XINT (left); + else + pt.x = last_mouse_motion_position.x; + top = Fcdr (Fassq (Qtop, parms)); + if (INTEGERP (top)) + pt.y = XINT (top); + else + pt.y = last_mouse_motion_position.y; + + /* Convert to screen coordinates */ + pt = [view convertPoint: pt toView: nil]; + pt = [[view window] convertBaseToScreen: pt]; + + /* Ensure in bounds. (Note, screen origin = lower left.) */ + if (pt.x + XINT (dx) <= 0) + *root_x = 0; /* Can happen for negative dx */ + else if (pt.x + XINT (dx) + width <= FRAME_NS_DISPLAY_INFO (f)->width) + /* It fits to the right of the pointer. */ + *root_x = pt.x + XINT (dx); + else if (width + XINT (dx) <= pt.x) + /* It fits to the left of the pointer. */ + *root_x = pt.x - width - XINT (dx); + else + /* Put it left justified on the screen -- it ought to fit that way. */ + *root_x = 0; + + if (pt.y - XINT (dy) - height >= 0) + /* It fits below the pointer. */ + *root_y = pt.y - height - XINT (dy); + else if (pt.y + XINT (dy) + height <= FRAME_NS_DISPLAY_INFO (f)->height) + /* It fits above the pointer */ + *root_y = pt.y + XINT (dy); + else + /* Put it on the top. */ + *root_y = FRAME_NS_DISPLAY_INFO (f)->height - height; +} + + +DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, + doc: /* Show STRING in a "tooltip" window on frame FRAME. +A tooltip window is a small window displaying a string. + +FRAME nil or omitted means use the selected frame. + +PARMS is an optional list of frame parameters which can be used to +change the tooltip's appearance. + +Automatically hide the tooltip after TIMEOUT seconds. TIMEOUT nil +means use the default timeout of 5 seconds. + +If the list of frame parameters PARMS contains a `left' parameter, +the tooltip is displayed at that x-position. Otherwise it is +displayed at the mouse position, with offset DX added (default is 5 if +DX isn't specified). Likewise for the y-position; if a `top' frame +parameter is specified, it determines the y-position of the tooltip +window, otherwise it is displayed at the mouse position, with offset +DY added (default is -10). + +A tooltip's maximum size is specified by `x-max-tooltip-size'. +Text larger than the specified size is clipped. */) + (string, frame, parms, timeout, dx, dy) + Lisp_Object string, frame, parms, timeout, dx, dy; +{ + int root_x, root_y; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + int count = SPECPDL_INDEX (); + struct frame *f; + char *str; + NSSize size; + + specbind (Qinhibit_redisplay, Qt); + + GCPRO4 (string, parms, frame, timeout); + + CHECK_STRING (string); + str = XSTRING (string)->data; + f = check_x_frame (frame); + if (NILP (timeout)) + timeout = make_number (5); + else + CHECK_NATNUM (timeout); + + if (NILP (dx)) + dx = make_number (5); + else + CHECK_NUMBER (dx); + + if (NILP (dy)) + dy = make_number (-10); + else + CHECK_NUMBER (dy); + + BLOCK_INPUT; + if (ns_tooltip == nil) + ns_tooltip = [[EmacsTooltip alloc] init]; + else + Fx_hide_tip (); + + [ns_tooltip setText: str]; + size = [ns_tooltip frame].size; + + /* Move the tooltip window where the mouse pointer is. Resize and + show it. */ + compute_tip_xy (f, parms, dx, dy, (int)size.width, (int)size.height, + &root_x, &root_y); + + [ns_tooltip showAtX: root_x Y: root_y for: XINT (timeout)]; + UNBLOCK_INPUT; + + UNGCPRO; + return unbind_to (count, Qnil); +} + + +DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, + doc: /* Hide the current tooltip window, if there is any. +Value is t if tooltip was open, nil otherwise. */) + () +{ + if (ns_tooltip == nil || ![ns_tooltip isActive]) + return Qnil; + [ns_tooltip hide]; + return Qt; +} + + +/* ========================================================================== + + Lisp interface declaration + + ========================================================================== */ + + +void +syms_of_nsfns () +{ + int i; + + Qns_frame_parameter = intern ("ns-frame-parameter"); + staticpro (&Qns_frame_parameter); + Qnone = intern ("none"); + staticpro (&Qnone); + Qbuffered = intern ("bufferd"); + staticpro (&Qbuffered); + Qfontsize = intern ("fontsize"); + staticpro (&Qfontsize); + + DEFVAR_LISP ("ns-icon-type-alist", &Vns_icon_type_alist, + "Alist of elements (REGEXP . IMAGE) for images of icons associated to\n\ +frames. If the title of a frame matches REGEXP, then IMAGE.tiff is\n\ +selected as the image of the icon representing the frame when it's\n\ +miniaturized. If an element is t, then Emacs tries to select an icon\n\ +based on the filetype of the visited file.\n\ +\n\ +The images have to be installed in a folder called English.lproj in the\n\ +Emacs.app folder. You have to restart Emacs after installing new icons.\n\ +\n\ +Example: Install an icon Gnus.tiff and execute the following code\n\ +\n\ + (setq ns-icon-type-alist\n\ + (append ns-icon-type-alist\n\ + '((\"^\\\\*\\\\(Group\\\\*$\\\\|Summary \\\\|Article\\\\*$\\\\)\"\n\ + . \"Gnus\"))))\n\ +\n\ +When you miniaturize a Group, Summary or Article frame, Gnus.tiff will\n\ +be used as the image of the icon representing the frame."); + Vns_icon_type_alist = Fcons (Qt, Qnil); + + defsubr (&Sns_read_file_name); + defsubr (&Sns_get_resource); + defsubr (&Sns_set_resource); + defsubr (&Sxw_display_color_p); /* this and next called directly by C code */ + defsubr (&Sx_display_grayscale_p); + defsubr (&Sns_list_fonts); + defsubr (&Sns_font_name); + defsubr (&Sns_list_colors); + defsubr (&Sns_color_defined_p); + defsubr (&Sns_color_values); + defsubr (&Sns_server_max_request_size); + defsubr (&Sns_server_vendor); + defsubr (&Sns_server_version); + defsubr (&Sns_display_pixel_width); + defsubr (&Sns_display_pixel_height); + defsubr (&Sns_display_usable_bounds); + defsubr (&Sns_display_mm_width); + defsubr (&Sns_display_mm_height); + defsubr (&Sns_display_screens); + defsubr (&Sns_display_planes); + defsubr (&Sns_display_color_cells); + defsubr (&Sns_display_visual_class); + defsubr (&Sns_display_backing_store); + defsubr (&Sns_display_save_under); + defsubr (&Sns_create_frame); + defsubr (&Sns_set_alpha); + defsubr (&Sns_open_connection); + defsubr (&Sns_close_connection); + defsubr (&Sns_display_list); + + defsubr (&Sns_hide_others); + defsubr (&Sns_hide_emacs); + defsubr (&Sns_emacs_info_panel); + defsubr (&Sns_list_services); + defsubr (&Sns_perform_service); + defsubr (&Sns_convert_utf8_nfd_to_nfc); + defsubr (&Sns_focus_frame); + defsubr (&Sns_popup_prefs_panel); + defsubr (&Sns_popup_font_panel); + defsubr (&Sns_popup_color_panel); + + defsubr (&Sx_show_tip); + defsubr (&Sx_hide_tip); + + /* used only in fontset.c */ + check_window_system_func = check_ns; + +} + + + +/* ========================================================================== + + Class implementations + + ========================================================================== */ + + +@implementation EmacsSavePanel +#ifdef NS_IMPL_COCOA +/* -------------------------------------------------------------------------- + These are overridden to intercept on OS X: ending panel restarts NSApp + event loop if it is stopped. Not sure if this is correct behavior, + perhaps should check if running and if so send an appdefined. + -------------------------------------------------------------------------- */ +- (void) ok: (id)sender +{ + [super ok: sender]; + panelOK = 1; + [NSApp stop: self]; +} +- (void) cancel: (id)sender +{ + [super cancel: sender]; + [NSApp stop: self]; +} +#endif +@end + + +@implementation EmacsOpenPanel +#ifdef NS_IMPL_COCOA +/* -------------------------------------------------------------------------- + These are overridden to intercept on OS X: ending panel restarts NSApp + event loop if it is stopped. Not sure if this is correct behavior, + perhaps should check if running and if so send an appdefined. + -------------------------------------------------------------------------- */ +- (void) ok: (id)sender +{ + [super ok: sender]; + panelOK = 1; + [NSApp stop: self]; +} +- (void) cancel: (id)sender +{ + [super cancel: sender]; + [NSApp stop: self]; +} +#endif +@end + + +@implementation EmacsFileDelegate +/* -------------------------------------------------------------------------- + Delegate methods for Open/Save panels + -------------------------------------------------------------------------- */ +- (BOOL)panel: (id)sender isValidFilename: (NSString *)filename +{ + return YES; +} +- (BOOL)panel: (id)sender shouldShowFilename: (NSString *)filename +{ + return YES; +} +- (NSString *)panel: (id)sender userEnteredFilename: (NSString *)filename + confirmed: (BOOL)okFlag +{ + return filename; +} +@end + +#endif diff --git a/src/nsgui.h b/src/nsgui.h new file mode 100644 index 00000000000..df38db84ade --- /dev/null +++ b/src/nsgui.h @@ -0,0 +1,208 @@ +/* Definitions and headers for communication on the NeXT/Open/GNUstep API. + Copyright (C) 1995, 2005, 2008 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#ifndef __NSGUI_H__ +#define __NSGUI_H__ + +/* this gets included from a couple of the plain (non-NS) .c files */ +#ifdef __OBJC__ + +#ifdef NS_IMPL_COCOA +#ifdef Z +#warning "Z is defined. If you get a later parse error in a header, check that buffer.h or other files #define-ing Z are not included." +#endif /* Z */ +#define Cursor FooFoo +#undef init_process +#endif /* NS_IMPL_COCOA */ + +#ifdef NS_IMPL_GNUSTEP +#undef hash_remove +#endif + +#import <AppKit/AppKit.h> + +#ifdef NS_IMPL_GNUSTEP +#define hash_remove emacs_hash_remove +#endif + +#ifdef NS_IMPL_COCOA +#undef Cursor +#define init_process emacs_init_process +#endif /* NS_IMPL_COCOA */ +#import <Foundation/NSDistantObject.h> + +#ifdef NS_IMPL_COCOA +#include <AvailabilityMacros.h> +#endif /* NS_IMPL_COCOA */ + +#endif /* __OBJC__ */ + + +/* menu-related */ +#define free_widget_value(wv) xfree (wv) +#define malloc_widget_value() ((widget_value *) memset (xmalloc \ + (sizeof (widget_value)), 0, sizeof (widget_value))) + +/* Emulate XCharStruct. */ +typedef struct _XCharStruct +{ + int rbearing; + int lbearing; + int width; + int ascent; + int descent; +} XCharStruct; + +/* 23: Fake tructure from Xlib.h to represent two-byte characters. */ +#ifndef __OBJC__ +typedef unsigned short unichar; +#endif +typedef unichar XChar2b; + +#define STORE_XCHAR2B(chp, b1, b2) \ + (*(chp) = ((XChar2b)((((b1) & 0x00ff) << 8) | ((b2) & 0x00ff)))) + +#define XCHAR2B_BYTE1(chp) \ + (((*chp) & 0xff00) >> 8) + +#define XCHAR2B_BYTE2(chp) \ + ((*chp) & 0x00ff) + +#define FACE_DEFAULT (~0) + + +/* PENDING: xfaces requires these structures, but the question is are we + forced to use them? */ +typedef struct _XGCValues +{ +#ifdef __OBJC__ + NSColor *foreground; + NSColor *background; + struct ns_font *font; +#else + void *foreground; + void *background; + void *font; +#endif +} XGCValues; + +typedef XGCValues * GC; + +#define GCForeground 0x01 +#define GCBackground 0x02 +#define GCFont 0x03 + +#ifdef __OBJC__ +typedef id Pixmap; +#else +typedef void *Pixmap; +#endif + +#ifdef __OBJC__ +typedef NSCursor * Cursor; +#else +typedef void *Cursor; +#endif + +#define No_Cursor (0) + +#ifdef __OBJC__ +typedef NSColor * Color; +#else +typedef void * Color; +#endif +typedef int Window; +typedef int Display; + +/* Xism */ +typedef Lisp_Object XrmDatabase; + + +/* 23: some sort of attempt to normalize rectangle handling.. seems a bit much + for what is accomplished */ +typedef struct { + int x, y; + unsigned width, height; +} XRectangle; + +#ifndef __OBJC__ +typedef struct _NSPoint { float x, y; } NSPoint; +typedef struct _NSSize { float width, height; } NSSize; +typedef struct _NSRect { NSPoint origin; NSSize size; } NSRect; +#endif + +#define NativeRectangle struct _NSRect + +#define CONVERT_TO_XRECT(xr, nr) \ + ((xr).x = (nr).origin.x, \ + (xr).y = (nr).origin.y, \ + (xr).width = (nr).size.width, \ + (xr).height = (nr).size.height) + +#define CONVERT_FROM_XRECT(xr, nr) \ + ((nr).origin.x = (xr).x, \ + (nr).origin.y = (xr).y, \ + (nr).size.width = (xr).width, \ + (nr).size.height = (xr).height) + +#define STORE_NATIVE_RECT(nr, px, py, pwidth, pheight) \ + ((nr).origin.x = (px), \ + (nr).origin.y = (py), \ + (nr).size.width = (pwidth), \ + (nr).size.height = (pheight)) + + + + +/* This stuff needed by frame.c. */ +#define ForgetGravity 0 +#define NorthWestGravity 1 +#define NorthGravity 2 +#define NorthEastGravity 3 +#define WestGravity 4 +#define CenterGravity 5 +#define EastGravity 6 +#define SouthWestGravity 7 +#define SouthGravity 8 +#define SouthEastGravity 9 +#define StaticGravity 10 + +#define NoValue 0x0000 +#define XValue 0x0001 +#define YValue 0x0002 +#define WidthValue 0x0004 +#define HeightValue 0x0008 +#define AllValues 0x000F +#define XNegative 0x0010 +#define YNegative 0x0020 + +#define USPosition (1L << 0) /* user specified x, y */ +#define USSize (1L << 1) /* user specified width, height */ + +#define PPosition (1L << 2) /* program specified position */ +#define PSize (1L << 3) /* program specified size */ +#define PMinSize (1L << 4) /* program specified minimum size */ +#define PMaxSize (1L << 5) /* program specified maximum size */ +#define PResizeInc (1L << 6) /* program specified resize increments */ +#define PAspect (1L << 7) /* program specified min, max aspect ratios */ +#define PBaseSize (1L << 8) /* program specified base for incrementing */ +#define PWinGravity (1L << 9) /* program specified window gravity */ + +#endif /* __NSGUI_H__ */ diff --git a/src/nsimage.m b/src/nsimage.m new file mode 100644 index 00000000000..692190280dc --- /dev/null +++ b/src/nsimage.m @@ -0,0 +1,480 @@ +/* Image support for the NeXT/Open/GNUstep and MacOSX window system. + Copyright (C) 1989, 1992, 1993, 1994, 2005, 2006, 2008, + Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +Originally by Carl Edman +Updated by Christian Limpach (chris@nice.ch) +OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com) +MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net) +GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu) + +*/ + +#include "config.h" +#include "lisp.h" +#include "dispextern.h" +#include "nsterm.h" +#include "frame.h" + +extern Lisp_Object QCfile, QCdata; + +/* call tracing */ +#if 0 +int image_trace_num = 0; +#define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \ + __FILE__, __LINE__, ++image_trace_num) +#else +#define NSTRACE(x) +#endif + + +/* ========================================================================== + + C interface. This allows easy calling from C files. We could just + compile everything as Objective-C, but that might mean slower + compilation and possible difficulties on some platforms.. + + ========================================================================== */ + +void * +ns_image_from_XBM (unsigned char *bits, int width, int height) +{ + NSTRACE (ns_image_from_XBM); + return [[EmacsImage alloc] initFromXBM: bits + width: width height: height + flip: YES]; +} + +void * +ns_image_for_XPM (int width, int height, int depth) +{ + NSTRACE (ns_image_for_XPM); + return [[EmacsImage alloc] initForXPMWithDepth: depth + width: width height: height]; +} + +void * +ns_image_from_file (Lisp_Object file) +{ + NSTRACE (ns_image_from_bitmap_file); + return [EmacsImage allocInitFromFile: file]; +} + +int +ns_load_image (struct frame *f, struct image *img, + Lisp_Object spec_file, Lisp_Object spec_data) +{ + NSTRACE (ns_load_image); + + EmacsImage *eImg; + NSSize size; + + if (NILP (spec_data)) + { + eImg = [EmacsImage allocInitFromFile: spec_file]; + } + else + { + NSData *data = [NSData dataWithBytes: XSTRING (spec_data)->data + length: SBYTES (spec_data)]; + eImg = [[EmacsImage alloc] initWithData: data]; + [eImg setPixmapData]; + } + + if (eImg == nil) + { + add_to_log ("Unable to load image %s", img->spec, Qnil); + return 0; + } + + size = [eImg size]; + img->width = size.width; + img->height = size.height; + + /* 4) set img->pixmap = emacsimage */ + img->pixmap = eImg; + return 1; +} + + +int +ns_image_width (void *img) +{ + return [(id)img size].width; +} + +int +ns_image_height (void *img) +{ + return [(id)img size].height; +} + +unsigned long +ns_get_pixel (void *img, int x, int y) +{ + return [(EmacsImage *)img getPixelAtX: x Y: y]; +} + +void +ns_put_pixel (void *img, int x, int y, unsigned long argb) +{ + unsigned char alpha = (argb >> 24) & 0xFF; + if (alpha == 0) + alpha = 0xFF; + [(EmacsImage *)img setPixelAtX: x Y: y toRed: (argb >> 16) & 0xFF + green: (argb >> 8) & 0xFF blue: (argb & 0xFF) alpha: alpha]; +} + +void +ns_set_alpha (void *img, int x, int y, unsigned char a) +{ + [(EmacsImage *)img setAlphaAtX: x Y: y to: a]; +} + + +/* ========================================================================== + + Class supporting bitmaps and images of various sorts. + + ========================================================================== */ + +@implementation EmacsImage + +static EmacsImage *ImageList = nil; + ++ allocInitFromFile: (Lisp_Object)file +{ + EmacsImage *image = ImageList; + Lisp_Object found; + + /* look for an existing image of the same name */ + while (image != nil && + [[image name] compare: [NSString stringWithUTF8String: SDATA (file)]] + != NSOrderedSame) + image = [image imageListNext]; + + if (image != nil) + { + [image reference]; + return image; + } + + /* Search bitmap-file-path for the file, if appropriate. */ + found = x_find_image_file (file); + if (!STRINGP (found)) + return nil; + + image = [[EmacsImage alloc] initByReferencingFile: + [NSString stringWithUTF8String: SDATA (found)]]; + + if ([image bestRepresentationForDevice: nil] == nil) + { + [image release]; + return nil; + } + + [image setName: [NSString stringWithUTF8String: SDATA (file)]]; + [image reference]; + ImageList = [image imageListSetNext: ImageList]; + + return image; +} + + +- reference +{ + refCount++; + return self; +} + + +- imageListSetNext: (id)arg +{ + imageListNext = arg; + return self; +} + + +- imageListNext +{ + return imageListNext; +} + + +- (void)dealloc +{ + id list = ImageList; + + if (refCount > 1) + { + refCount--; + return; + } + + [stippleMask release]; + + if (list == self) + ImageList = imageListNext; + else + { + while (list != nil && [list imageListNext] != self) + list = [list imageListNext]; + [list imageListSetNext: imageListNext]; + } + + [super dealloc]; +} + + +- initFromXBM: (unsigned char *)bits width: (int)w height: (int)h + flip: (BOOL)flip +{ + return [self initFromSkipXBM: bits width: w height: h flip: flip length: 0]; +} + + +- initFromSkipXBM: (unsigned char *)bits width: (int)w height: (int)h + flip: (BOOL)flip length: (int)length; +{ + int bpr = (w + 7) / 8; + unsigned char *planes[5]; + + [self initWithSize: NSMakeSize (w, h)]; + + bmRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL + pixelsWide: w pixelsHigh: h + bitsPerSample: 8 samplesPerPixel: 4 + hasAlpha: YES isPlanar: YES + colorSpaceName: NSCalibratedRGBColorSpace + bytesPerRow: w bitsPerPixel: 0]; + + [bmRep getBitmapDataPlanes: planes]; + { + /* pull bits out to set the (bytewise) alpha mask */ + int i, j, k; + unsigned char *s = bits; + unsigned char *alpha = planes[3]; + unsigned char swt[16] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, + 3, 11, 7, 15}; + unsigned char c, bitPat; + + for (j = 0; j < h; j++) + for (i = 0; i < bpr; i++) + { + if (length) + { + unsigned char s1, s2; + while (*s++ != 'x' && s < bits + length); + if (s >= bits + length) + { + [bmRep release]; + return nil; + } +#define hexchar(x) (isdigit (x) ? x - '0' : x - 'a' + 10) + s1 = *s++; + s2 = *s++; + c = hexchar (s1) * 0x10 + hexchar (s2); + } + else + c = *s++; + + bitPat = flip ? swt[c >> 4] | (swt[c & 0xf] << 4) : c ^ 255; + for (k =0; k<8; k++) + { + *alpha++ = (bitPat & 0x80) ? 0xff : 0; + bitPat <<= 1; + } + } + } + + [self addRepresentation: bmRep]; + + bzero (planes[0], w*h); + bzero (planes[1], w*h); + bzero (planes[2], w*h); + [self setXBMColor: [NSColor blackColor]]; + return self; +} + + +/* Set color for a bitmap image (see initFromSkipXBM). Note that the alpha + is used as a mask, so we just memset the entire array. */ +- setXBMColor: (NSColor *)color +{ + NSSize s = [self size]; + int len = (int) s.width * s.height; + unsigned char *planes[5]; + float r, g, b, a; + NSColor *rgbColor; + + if (bmRep == nil || color == nil) + return; + + if ([color colorSpaceName] != NSCalibratedRGBColorSpace) + rgbColor = [color colorUsingColorSpaceName: NSCalibratedRGBColorSpace]; + else + rgbColor = color; + + [rgbColor getRed: &r green: &g blue: &b alpha: &a]; + + [bmRep getBitmapDataPlanes: planes]; + + /* we used to just do this, but Cocoa seems to have a bug when rendering + an alpha-masked image onto a dark background where it bloats the mask */ + /* memset (planes[0..2], r, g, b*0xff, len); */ + { + int i, len = s.width*s.height; + int rr = r * 0xff, gg = g * 0xff, bb = b * 0xff; + for (i =0; i<len; i++) + if (planes[3][i] != 0) + { + planes[0][i] = rr; + planes[1][i] = gg; + planes[2][i] = bb; + } + } +} + + +- initForXPMWithDepth: (int)depth width: (int)width height: (int)height +{ + NSSize s = {width, height}; + int i; + + [self initWithSize: s]; + + bmRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL + pixelsWide: width pixelsHigh: height + /* keep things simple for now */ + bitsPerSample: 8 samplesPerPixel: 4 /*RGB+A*/ + hasAlpha: YES isPlanar: YES + colorSpaceName: NSCalibratedRGBColorSpace + bytesPerRow: width bitsPerPixel: 0]; + + [bmRep getBitmapDataPlanes: pixmapData]; + for (i =0; i<4; i++) + bzero (pixmapData[i], width*height); + [self addRepresentation: bmRep]; + return self; +} + + +/* attempt to pull out pixmap data from a BitmapImageRep; returns NO if fails */ +- (void) setPixmapData +{ + NSEnumerator *reps; + NSImageRep *rep; + + reps = [[self representations] objectEnumerator]; + while (rep = (NSImageRep *) [reps nextObject]) + { + if ([rep respondsToSelector: @selector (getBitmapDataPlanes:)]) + { + bmRep = (NSBitmapImageRep *) rep; + onTiger = [bmRep respondsToSelector: @selector (colorAtX:y:)]; + + if ([bmRep numberOfPlanes] >= 3) + [bmRep getBitmapDataPlanes: pixmapData]; + break; + } + } +} + + +/* note; this and next work only for image created with initForXPMWithDepth, + initFromSkipXBM, or where setPixmapData was called successfully */ +/* return ARGB */ +- (unsigned long) getPixelAtX: (int)x Y: (int)y +{ + if (bmRep == nil) + return 0; + + /* this method is faster but won't work for bitmaps */ + if (pixmapData[0] != NULL) + { + int loc = x + y * [self size].width; + return (pixmapData[3][loc] << 24) /* alpha */ + | (pixmapData[0][loc] << 16) | (pixmapData[1][loc] << 8) + | (pixmapData[2][loc]); + } + else if (onTiger) + { + NSColor *color = [bmRep colorAtX: x y: y]; + float r, g, b, a; + [color getRed: &r green: &g blue: &b alpha: &a]; + return ((int)(a * 255.0) << 24) + | ((int)(r * 255.0) << 16) | ((int)(g * 255.0) << 8) + | ((int)(b * 255.0)); + + } + return 0; +} + +- (void) setPixelAtX: (int)x Y: (int)y toRed: (unsigned char)r + green: (unsigned char)g blue: (unsigned char)b + alpha:(unsigned char)a; +{ + if (bmRep == nil) + return; + + if (pixmapData[0] != NULL) + { + int loc = x + y * [self size].width; + pixmapData[0][loc] = r; + pixmapData[1][loc] = g; + pixmapData[2][loc] = b; + pixmapData[3][loc] = a; + } + else if (onTiger) + { + [bmRep setColor: + [NSColor colorWithCalibratedRed: r green: g blue: b alpha: a] + atX: x y: y]; + } +} + +- (void) setAlphaAtX: (int) x Y: (int) y to: (unsigned char) a +{ + if (bmRep == nil) + return; + + if (pixmapData[0] != NULL) + { + int loc = x + y * [self size].width; + + pixmapData[3][loc] = a; + } + else if (onTiger) + { + NSColor *color = [bmRep colorAtX: x y: y]; + color = [color colorWithAlphaComponent: (a / 255.0)]; + [bmRep setColor: color atX: x y: y]; + } +} + +/* returns a pattern color, which is cached here */ +- (NSColor *)stippleMask +{ + if (stippleMask == nil) + stippleMask = [[NSColor colorWithPatternImage: self] retain]; + return stippleMask; +} + +@end diff --git a/src/nsmenu.m b/src/nsmenu.m new file mode 100644 index 00000000000..d59732b02e4 --- /dev/null +++ b/src/nsmenu.m @@ -0,0 +1,1948 @@ +/* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module. + Copyright (C) 2007, 2008 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +By Adrian Robert, based on code from original nsmenu.m (Carl Edman, +Christian Limpach, Scott Bender, Christophe de Dinechin) and code in the +Carbon version by Yamamoto Mitsuharu. */ + +#include "config.h" +#include "lisp.h" +#include "window.h" +#include "buffer.h" +#include "keymap.h" +#include "coding.h" +#include "commands.h" +#include "blockinput.h" +#include "nsterm.h" +#include "termhooks.h" +#include "keyboard.h" + +/* for profiling */ +#include <sys/timeb.h> +#include <sys/types.h> + +#define MenuStagger 10.0 + +#if 0 +int menu_trace_num = 0; +#define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \ + __FILE__, __LINE__, ++menu_trace_num) +#else +#define NSTRACE(x) +#endif + +#if 0 +/* Include lisp -> C common menu parsing code */ +#define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str) +#include "nsmenu_common.c" +#endif + +extern struct widget_value; + +extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook; +extern Lisp_Object QCtoggle, QCradio; + +extern Lisp_Object Vmenu_updating_frame; + +Lisp_Object Qdebug_on_next_call; +extern Lisp_Object Voverriding_local_map, Voverriding_local_map_menu_flag, + Qoverriding_local_map, Qoverriding_terminal_local_map; + +extern long context_menu_value; +EmacsMenu *mainMenu, *svcsMenu; + +/* NOTE: toolbar implementation is at end, + following complete menu implementation. */ + + +/* ========================================================================== + + Menu: Externally-called functions + + ========================================================================== */ + + +/*23: PENDING: not currently used, but should normalize with other terms. */ +void +x_activate_menubar (struct frame *f) +{ + fprintf (stderr, "XXX: Received x_activate_menubar event.\n"); +} + + +/* Supposed to discard menubar and free storage. Since we share the + menubar among frames and update its context for the focused window, + there is nothing to do here. */ +void +free_frame_menubar (struct frame *f) +{ + return; +} + + +/* -------------------------------------------------------------------------- + Update menubar. Three cases: + 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up + just top-level menu strings (OS X), or goto case (2) (GNUstep). + 2) deep_p = 1, submenu = nil: Recompute all submenus. + 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu. + -------------------------------------------------------------------------- */ +/*#define NSMENUPROFILE 1 */ +void +ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu) +{ + NSAutoreleasePool *pool; + id menu = [NSApp mainMenu]; + static EmacsMenu *last_submenu = nil; + BOOL needsSet = NO; + const char *submenuTitle = [[submenu title] UTF8String]; + extern int waiting_for_input; + int owfi; + Lisp_Object items; + widget_value *wv, *first_wv, *prev_wv = 0; + int i; + +#ifdef NSMENUPROFILE + struct timeb tb; + long t; +#endif + + NSTRACE (set_frame_menubar); + + if (f != SELECTED_FRAME ()) + return; + XSETFRAME (Vmenu_updating_frame, f); +/*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */ + + BLOCK_INPUT; + pool = [[NSAutoreleasePool alloc] init]; + + /* Menu may have been created automatically; if so, discard it. */ + if ([menu isKindOfClass: [EmacsMenu class]] == NO) + { + [menu release]; + menu = nil; + } + + if (menu == nil) + { + menu = [[EmacsMenu alloc] initWithTitle: @"Emacs"]; + needsSet = YES; + } + else + { /* close up anything on there */ + id attMenu = [menu attachedMenu]; + if (attMenu != nil) + [attMenu close]; + } + +#ifdef NSMENUPROFILE + ftime (&tb); + t = -(1000*tb.time+tb.millitm); +#endif + + /* widget_value is a straightforward object translation of emacs's + Byzantine lisp menu structures */ + wv = xmalloc_widget_value (); + wv->name = "Emacs"; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + first_wv = wv; + +#ifdef NS_IMPL_GNUSTEP + deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */ +#endif + + if (deep_p) + { + /* Fully parse one or more of the submenus. */ + int n = 0; + int *submenu_start, *submenu_end; + int *submenu_top_level_items, *submenu_n_panes; + struct buffer *prev = current_buffer; + Lisp_Object buffer; + int specpdl_count = SPECPDL_INDEX (); + int previous_menu_items_used = f->menu_bar_items_used; + Lisp_Object *previous_items + = (Lisp_Object *) alloca (previous_menu_items_used + * sizeof (Lisp_Object)); + + /* lisp preliminaries */ + buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer; + specbind (Qinhibit_quit, Qt); + specbind (Qdebug_on_next_call, Qnil); + record_unwind_save_match_data (); + if (NILP (Voverriding_local_map_menu_flag)) + { + specbind (Qoverriding_terminal_local_map, Qnil); + specbind (Qoverriding_local_map, Qnil); + } + set_buffer_internal_1 (XBUFFER (buffer)); + + /* PENDING: for some reason this is not needed in other terms, + but some menu updates call Info-extract-pointer which causes + abort-on-error if waiting-for-input. Needs further investigation. */ + owfi = waiting_for_input; + waiting_for_input = 0; + + /* lucid hook and possible reset */ + safe_run_hooks (Qactivate_menubar_hook); + if (! NILP (Vlucid_menu_bar_dirty_flag)) + call0 (Qrecompute_lucid_menubar); + safe_run_hooks (Qmenu_bar_update_hook); + FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f)); + + /* Now ready to go */ + items = FRAME_MENU_BAR_ITEMS (f); + + /* Save the frame's previous menu bar contents data */ + if (previous_menu_items_used) + bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items, + previous_menu_items_used * sizeof (Lisp_Object)); + + /* parse stage 1: extract from lisp */ + save_menu_items (); + + menu_items = f->menu_bar_vector; + menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0; + submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); + submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); + submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int)); + submenu_top_level_items + = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); + init_menu_items (); + for (i = 0; i < XVECTOR (items)->size; i += 4) + { + Lisp_Object key, string, maps; + + key = XVECTOR (items)->contents[i]; + string = XVECTOR (items)->contents[i + 1]; + maps = XVECTOR (items)->contents[i + 2]; + if (NILP (string)) + break; + + /* PENDING: we'd like to only parse the needed submenu, but this + was causing crashes in the _common parsing code.. need to make + sure proper initialization done.. */ +/* if (submenu && strcmp (submenuTitle, SDATA (string))) + continue; */ + + submenu_start[i] = menu_items_used; + + menu_items_n_panes = 0; + submenu_top_level_items[i] = parse_single_submenu (key, string, maps); + submenu_n_panes[i] = menu_items_n_panes; + submenu_end[i] = menu_items_used; + n++; + } + + finish_menu_items (); + waiting_for_input = owfi; + + + if (submenu && n == 0) + { + /* should have found a menu for this one but didn't */ + fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n", + submenuTitle); + discard_menu_items (); + unbind_to (specpdl_count, Qnil); + [pool release]; + UNBLOCK_INPUT; + return; + } + + /* parse stage 2: insert into lucid 'widget_value' structures + [comments in other terms say not to evaluate lisp code here] */ + wv = xmalloc_widget_value (); + wv->name = "menubar"; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + first_wv = wv; + + for (i = 0; i < 4*n; i += 4) + { + menu_items_n_panes = submenu_n_panes[i]; + wv = digest_single_submenu (submenu_start[i], submenu_end[i], + submenu_top_level_items[i]); + if (prev_wv) + prev_wv->next = wv; + else + first_wv->contents = wv; + /* Don't set wv->name here; GC during the loop might relocate it. */ + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + prev_wv = wv; + } + + set_buffer_internal_1 (prev); + + /* Compare the new menu items with previous, and leave off if no change */ + /* PENDING: following other terms here, but seems like this should be + done before parse stage 2 above, since its results aren't used */ + if (previous_menu_items_used + && (!submenu || (submenu && submenu == last_submenu)) + && menu_items_used == previous_menu_items_used) + { + for (i = 0; i < previous_menu_items_used; i++) + /* PENDING: this ALWAYS fails on Buffers menu items.. something + about their strings causes them to change every time, so we + double-check failures */ + if (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])) + if (!(STRINGP (previous_items[i]) + && STRINGP (XVECTOR (menu_items)->contents[i]) + && !strcmp (SDATA (previous_items[i]), + SDATA (XVECTOR (menu_items)->contents[i])))) + break; + if (i == previous_menu_items_used) + { + /* No change.. */ + +#ifdef NSMENUPROFILE + ftime (&tb); + t += 1000*tb.time+tb.millitm; + fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t); +#endif + + free_menubar_widget_value_tree (first_wv); + discard_menu_items (); + unbind_to (specpdl_count, Qnil); + [pool release]; + UNBLOCK_INPUT; + return; + } + } + /* The menu items are different, so store them in the frame */ + /* PENDING: this is not correct for single-submenu case */ + f->menu_bar_vector = menu_items; + f->menu_bar_items_used = menu_items_used; + + /* Calls restore_menu_items, etc., as they were outside */ + unbind_to (specpdl_count, Qnil); + + /* Parse stage 2a: now GC cannot happen during the lifetime of the + widget_value, so it's safe to store data from a Lisp_String */ + wv = first_wv->contents; + for (i = 0; i < XVECTOR (items)->size; i += 4) + { + Lisp_Object string; + string = XVECTOR (items)->contents[i + 1]; + if (NILP (string)) + break; +/* if (submenu && strcmp (submenuTitle, SDATA (string))) + continue; */ + + wv->name = (char *) SDATA (string); + update_submenu_strings (wv->contents); + wv = wv->next; + } + + /* Now, update the NS menu; if we have a submenu, use that, otherwise + create a new menu for each sub and fill it. */ + if (submenu) + { + for (wv = first_wv->contents; wv; wv = wv->next) + { + if (!strcmp (submenuTitle, wv->name)) + { + [submenu fillWithWidgetValue: wv->contents]; + last_submenu = submenu; + break; + } + } + } + else + { + [menu fillWithWidgetValue: first_wv->contents]; + } + + } + else + { + static int n_previous_strings = 0; + static char previous_strings[100][10]; + static struct frame *last_f = NULL; + int n; + Lisp_Object string; + + /* Make widget-value tree w/ just the top level menu bar strings */ + items = FRAME_MENU_BAR_ITEMS (f); + if (NILP (items)) + { + [pool release]; + UNBLOCK_INPUT; + return; + } + + + /* check if no change.. this mechanism is a bit rough, but ready */ + n = XVECTOR (items)->size / 4; + if (f == last_f && n_previous_strings == n) + { + for (i = 0; i<n; i++) + { + string = XVECTOR (items)->contents[4*i+1]; + + if (!string) + continue; + if (NILP (string)) + if (previous_strings[i][0]) + break; + else + continue; + if (strncmp (previous_strings[i], SDATA (string), 10)) + break; + } + + if (i == n) + { + [pool release]; + UNBLOCK_INPUT; + return; + } + } + + [menu clear]; + for (i = 0; i < XVECTOR (items)->size; i += 4) + { + string = XVECTOR (items)->contents[i + 1]; + if (NILP (string)) + break; + + if (n < 100) + strncpy (previous_strings[i/4], SDATA (string), 10); + + wv = xmalloc_widget_value (); + wv->name = (char *) SDATA (string); + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + wv->call_data = (void *) (EMACS_INT) (-1); + +#ifdef NS_IMPL_COCOA + /* we'll update the real copy under app menu when time comes */ + if (!strcmp ("Services", wv->name)) + { + /* but we need to make sure it will update on demand */ + [svcsMenu setFrame: f]; + [svcsMenu setDelegate: svcsMenu]; + } + else +#endif + [menu addSubmenuWithTitle: wv->name forFrame: f]; + + if (prev_wv) + prev_wv->next = wv; + else + first_wv->contents = wv; + prev_wv = wv; + } + + last_f = f; + if (n < 100) + n_previous_strings = n; + else + n_previous_strings = 0; + + } + free_menubar_widget_value_tree (first_wv); + + +#ifdef NSMENUPROFILE + ftime (&tb); + t += 1000*tb.time+tb.millitm; + fprintf (stderr, "Menu update took %ld msec.\n", t); +#endif + + /* set main menu */ + if (needsSet) + [NSApp setMainMenu: menu]; + + [pool release]; + UNBLOCK_INPUT; + +} + + +/* Main emacs core entry point for menubar menus: called to indicate that the + frame's menus have changed, and the *step representation should be updated + from Lisp. */ +void +set_frame_menubar (struct frame *f, int first_time, int deep_p) +{ + ns_update_menubar (f, deep_p, nil); +} + + +/* Utility (from macmenu.c): is this item a separator? */ +static int +name_is_separator (name) + const char *name; +{ + const char *start = name; + + /* Check if name string consists of only dashes ('-'). */ + while (*name == '-') name++; + /* Separators can also be of the form "--:TripleSuperMegaEtched" + or "--deep-shadow". We don't implement them yet, se we just treat + them like normal separators. */ + return (*name == '\0' || start + 2 == name); +} + + +/* ========================================================================== + + Menu: class implementation + + ========================================================================== */ + + +/* Menu that can define itself from Emacs "widget_value"s and will lazily + update itself when user clicked. Based on Carbon/AppKit implementation + by Yamamoto Mitsuharu. */ +@implementation EmacsMenu + +/* override designated initializer */ +- initWithTitle: (NSString *)title +{ + if (self = [super initWithTitle: title]) + [self setAutoenablesItems: NO]; + return self; +} + + +/* used for top-level */ +- initWithTitle: (NSString *)title frame: (struct frame *)f +{ + [self initWithTitle: title]; + frame = f; +#ifdef NS_IMPL_COCOA + [self setDelegate: self]; +#endif + return self; +} + + +- (void)setFrame: (struct frame *)f +{ + frame = f; +} + + +/* delegate method called when a submenu is being opened: run a 'deep' call + to set_frame_menubar */ +- (void)menuNeedsUpdate: (NSMenu *)menu +{ + NSEvent *event = [[FRAME_NS_VIEW (frame) window] currentEvent]; + /* HACK: Cocoa/Carbon will request update on every keystroke + via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed + since key equivalents are handled through emacs. + On Leopard, even keystroke events generate SystemDefined events, but + their subtype is 8. */ + if ([event type] != NSSystemDefined || [event subtype] == 8) + return; +/*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */ + ns_update_menubar (frame, 1, self); +} + + +- (BOOL)performKeyEquivalent: (NSEvent *)theEvent +{ + if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ()) + && FRAME_NS_VIEW (SELECTED_FRAME ())) + [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent]; + return YES; +} + + +/* parse a wdiget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>') + into an accelerator string */ +-(NSString *)parseKeyEquiv: (char *)key +{ + char *tpos = key; + keyEquivModMask = 0; + /* currently we just parse 'super' combinations; + later we'll set keyEquivModMask */ + if (!key || !strlen (key)) + return @""; + + while (*tpos == ' ' || *tpos == '(') + tpos++; + if (*tpos != 's'/* || tpos[3] != ')'*/) + return @""; + return [NSString stringWithFormat: @"%c", tpos[2]]; +} + +- (id <NSMenuItem>)addItemWithWidgetValue: (void *)wvptr +{ + id <NSMenuItem> item; + widget_value *wv = (widget_value *)wvptr; + + if (name_is_separator (wv->name)) + { + item = [NSMenuItem separatorItem]; + [self addItem: item]; + } + else + { + NSString *title, *keyEq; + title = [NSString stringWithUTF8String: wv->name]; + if (title == nil) + title = @"< ? >"; /* (get out in the open so we know about it) */ + + keyEq = [self parseKeyEquiv: wv->key]; + + item = [self addItemWithTitle: (NSString *)title + action: @selector (menuDown:) + keyEquivalent: keyEq]; + if (keyEquivModMask) + [item setKeyEquivalentModifierMask: keyEquivModMask]; + + [item setEnabled: wv->enabled]; + + /* Draw radio buttons and tickboxes */ + if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE || + wv->button_type == BUTTON_TYPE_RADIO)) + [item setState: NSOnState]; + else + [item setState: NSOffState]; + + [item setTag: (int)wv->call_data]; + } + + return item; +} + + +/* convenience */ +-(void) clear +{ + int n; + + for (n = [self numberOfItems]-1; n >= 0; n--) + { + NSMenuItem *item = [self itemAtIndex: n]; + NSString *title = [item title]; + if (([title length] == 0 || [@"Apple" isEqualToString: title]) + && ![item isSeparatorItem]) + continue; + [self removeItemAtIndex: n]; + } +} + + +- (void)fillWithWidgetValue: (void *)wvptr +{ + widget_value *wv = (widget_value *)wvptr; + + /* clear existing contents */ + [self setMenuChangedMessagesEnabled: NO]; + [self clear]; + + /* add new contents */ + for (; wv != NULL; wv = wv->next) + { + id <NSMenuItem> item = [self addItemWithWidgetValue: wv]; + + if (wv->contents) + { + EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: @"Submenu"]; + + [self setSubmenu: submenu forItem: item]; + [submenu fillWithWidgetValue: wv->contents]; + [submenu release]; + [item setAction: nil]; + } + } + + [self setMenuChangedMessagesEnabled: YES]; +#ifdef NS_IMPL_GNUSTEP + if ([[self window] isVisible]) + [self sizeToFit]; +#else + if ([self supermenu] == nil) + [self sizeToFit]; +#endif +} + + +/* adds an empty submenu and returns it */ +- (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f +{ + NSString *titleStr = [NSString stringWithUTF8String: title]; + id <NSMenuItem> item = + [self addItemWithTitle: titleStr + action: nil /*@selector (menuDown:) */ + keyEquivalent: @""]; + EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f]; + [self setSubmenu: submenu forItem: item]; + [submenu release]; + return submenu; +} + +/* run a menu in popup mode */ +- (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f + keymaps: (int)keymaps +{ + EmacsView *view = FRAME_NS_VIEW (f); +/* p = [view convertPoint:p fromView: nil]; */ + p.y = NSHeight ([view frame]) - p.y; + NSEvent *e = [[view window] currentEvent]; + NSEvent *event = [NSEvent mouseEventWithType: NSRightMouseDown + location: p + modifierFlags: 0 + timestamp: [e timestamp] + windowNumber: [[view window] windowNumber] + context: [e context] + eventNumber: 0/*[e eventNumber] */ + clickCount: 1 + pressure: 0]; + long retVal; + + context_menu_value = -1; + [NSMenu popUpContextMenu: self withEvent: event forView: view]; + retVal = context_menu_value; + context_menu_value = 0; + return retVal > 0 ? + find_and_return_menu_selection (f, keymaps, (void *)retVal) : Qnil; +} + +@end /* EmacsMenu */ + + + +/* ========================================================================== + + Context Menu: implementing functions + + ========================================================================== */ + +static Lisp_Object +cleanup_popup_menu (Lisp_Object arg) +{ + discard_menu_items (); + return Qnil; +} + + +static Lisp_Object +ns_popup_menu (Lisp_Object position, Lisp_Object menu) +{ + EmacsMenu *pmenu; + struct frame *f = NULL; + NSPoint p; + Lisp_Object window, x, y, tem, keymap, title; + struct gcpro gcpro1; + int specpdl_count = SPECPDL_INDEX (), specpdl_count2; + char *error_name = NULL; + int keymaps = 0; + widget_value *wv, *first_wv = 0; + + NSTRACE (ns_popup_menu); + + if (!NILP (position)) + { + check_ns (); + + if (EQ (position, Qt) + || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) + || EQ (XCAR (position), Qtool_bar)))) + { + /* Use the mouse's current position. */ + struct frame *new_f = SELECTED_FRAME (); + + if (FRAME_TERMINAL (new_f)->mouse_position_hook) + (*FRAME_TERMINAL (new_f)->mouse_position_hook) + (&new_f, 0, 0, 0, &x, &y, 0); + if (new_f != 0) + XSETFRAME (window, new_f); + else + { + window = selected_window; + x = make_number (0); + y = make_number (0); + } + } + else + { + CHECK_CONS (position); + tem = Fcar (position); + if (XTYPE (tem) == Lisp_Cons) + { + window = Fcar (Fcdr (position)); + x = Fcar (tem); + y = Fcar (Fcdr (tem)); + } + else + { + tem = Fcar (Fcdr (position)); + window = Fcar (tem); + tem = Fcar (Fcdr (Fcdr (tem))); + x = Fcar (tem); + y = Fcdr (tem); + } + } + + CHECK_NUMBER (x); + CHECK_NUMBER (y); + + if (FRAMEP (window)) + { + f = XFRAME (window); + + p.x = 0; + p.y = 0; + } + else + { + struct window *win = XWINDOW (window); + CHECK_LIVE_WINDOW (window); + f = XFRAME (WINDOW_FRAME (win)); + p.x = FRAME_COLUMN_WIDTH (f) * WINDOW_LEFT_EDGE_COL (win); + p.y = FRAME_LINE_HEIGHT (f) * WINDOW_TOP_EDGE_LINE (win); + } + + p.x += XINT (x); p.y += XINT (y); + + XSETFRAME (Vmenu_updating_frame, f); + } + else + { /* no position given */ + /* PENDING: if called during dump, we need to stop precomputation of + key equivalents (see below) because the keydefs in ns-win.el have + not been loaded yet. */ + if (noninteractive) + return Qnil; + Vmenu_updating_frame = Qnil; + } + + /* now parse the lisp menus */ + record_unwind_protect (unuse_menu_items, Qnil); + title = Qnil; + GCPRO1 (title); + + /* Decode the menu items from what was specified. */ + + keymap = get_keymap (menu, 0, 0); + if (CONSP (keymap)) + { + /* We were given a keymap. Extract menu info from the keymap. */ + Lisp_Object prompt; + + /* Extract the detailed info to make one pane. */ + keymap_panes (&menu, 1, NILP (position)); + + /* Search for a string appearing directly as an element of the keymap. + That string is the title of the menu. */ + prompt = Fkeymap_prompt (keymap); + title = NILP (prompt) ? build_string ("Select") : prompt; + + /* Make that be the pane title of the first pane. */ + if (!NILP (prompt) && menu_items_n_panes >= 0) + XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt; + + keymaps = 1; + } + else if (CONSP (menu) && KEYMAPP (XCAR (menu))) + { + /* We were given a list of keymaps. */ + int nmaps = XFASTINT (Flength (menu)); + Lisp_Object *maps + = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object)); + int i; + + title = Qnil; + + /* The first keymap that has a prompt string + supplies the menu title. */ + for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem)) + { + Lisp_Object prompt; + + maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0); + + prompt = Fkeymap_prompt (keymap); + if (NILP (title) && !NILP (prompt)) + title = prompt; + } + + /* Extract the detailed info to make one pane. */ + keymap_panes (maps, nmaps, NILP (position)); + + /* Make the title be the pane title of the first pane. */ + if (!NILP (title) && menu_items_n_panes >= 0) + XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title; + + keymaps = 1; + } + else + { + /* We were given an old-fashioned menu. */ + title = Fcar (menu); + CHECK_STRING (title); + + list_of_panes (Fcdr (menu)); + + keymaps = 0; + } + + unbind_to (specpdl_count, Qnil); + + /* If no position given, that was a signal to just precompute and cache + key equivalents, which was a side-effect of what we just did. */ + if (NILP (position)) + { + discard_menu_items (); + UNGCPRO; + return Qnil; + } + + record_unwind_protect (cleanup_popup_menu, Qnil); + BLOCK_INPUT; + + /* now parse stage 2 as in ns_update_menubar */ + wv = xmalloc_widget_value (); + wv->name = "contextmenu"; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + first_wv = wv; + + specpdl_count2 = SPECPDL_INDEX (); + +#if 0 + /*PENDING: a couple of one-line differences prevent reuse */ + wv = digest_single_submenu (0, menu_items_used, Qnil); +#else + { + widget_value *save_wv = 0, *prev_wv = 0; + widget_value **submenu_stack + = (widget_value **) alloca (menu_items_used * sizeof (widget_value *)); +/* Lisp_Object *subprefix_stack + = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */ + int submenu_depth = 0; + int first_pane = 1; + int i; + + /* Loop over all panes and items, filling in the tree. */ + i = 0; + while (i < menu_items_used) + { + if (EQ (XVECTOR (menu_items)->contents[i], Qnil)) + { + submenu_stack[submenu_depth++] = save_wv; + save_wv = prev_wv; + prev_wv = 0; + first_pane = 1; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda)) + { + prev_wv = save_wv; + save_wv = submenu_stack[--submenu_depth]; + first_pane = 0; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qt) + && submenu_depth != 0) + i += MENU_ITEMS_PANE_LENGTH; + /* Ignore a nil in the item list. + It's meaningful only for dialog boxes. */ + else if (EQ (XVECTOR (menu_items)->contents[i], Qquote)) + i += 1; + else if (EQ (XVECTOR (menu_items)->contents[i], Qt)) + { + /* Create a new pane. */ + Lisp_Object pane_name, prefix; + char *pane_string; + + pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME); + prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name)) + { + pane_name = ENCODE_MENU_STRING (pane_name); + ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name); + } +#endif + pane_string = (NILP (pane_name) + ? "" : (char *) SDATA (pane_name)); + /* If there is just one top-level pane, put all its items directly + under the top-level menu. */ + if (menu_items_n_panes == 1) + pane_string = ""; + + /* If the pane has a meaningful name, + make the pane a top-level menu item + with its items as a submenu beneath it. */ + if (!keymaps && strcmp (pane_string, "")) + { + wv = xmalloc_widget_value (); + if (save_wv) + save_wv->next = wv; + else + first_wv->contents = wv; + wv->name = pane_string; + if (keymaps && !NILP (prefix)) + wv->name++; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + save_wv = wv; + prev_wv = 0; + } + else if (first_pane) + { + save_wv = wv; + prev_wv = 0; + } + first_pane = 0; + i += MENU_ITEMS_PANE_LENGTH; + } + else + { + /* Create a new item within current pane. */ + Lisp_Object item_name, enable, descrip, def, type, selected, help; + item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); + enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); + descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); + def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); + type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE); + selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED); + help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRINGP (item_name) && STRING_MULTIBYTE (item_name)) + { + item_name = ENCODE_MENU_STRING (item_name); + ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name); + } + + if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) + { + descrip = ENCODE_MENU_STRING (descrip); + ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip); + } +#endif /* not HAVE_MULTILINGUAL_MENU */ + + wv = xmalloc_widget_value (); + if (prev_wv) + prev_wv->next = wv; + else + save_wv->contents = wv; + wv->name = (char *) SDATA (item_name); + if (!NILP (descrip)) + wv->key = (char *) SDATA (descrip); + wv->value = 0; + /* If this item has a null value, + make the call_data null so that it won't display a box + when the mouse is on it. */ + wv->call_data = + !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0; + wv->enabled = !NILP (enable); + + if (NILP (type)) + wv->button_type = BUTTON_TYPE_NONE; + else if (EQ (type, QCtoggle)) + wv->button_type = BUTTON_TYPE_TOGGLE; + else if (EQ (type, QCradio)) + wv->button_type = BUTTON_TYPE_RADIO; + else + abort (); + + wv->selected = !NILP (selected); + + if (! STRINGP (help)) + help = Qnil; + + wv->help = help; + + prev_wv = wv; + + i += MENU_ITEMS_ITEM_LENGTH; + } + } + } +#endif + + if (!NILP (title)) + { + widget_value *wv_title = xmalloc_widget_value (); + widget_value *wv_sep = xmalloc_widget_value (); + + /* Maybe replace this separator with a bitmap or owner-draw item + so that it looks better. Having two separators looks odd. */ + wv_sep->name = "--"; + wv_sep->next = first_wv->contents; + wv_sep->help = Qnil; + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRING_MULTIBYTE (title)) + title = ENCODE_MENU_STRING (title); +#endif + + wv_title->name = (char *) SDATA (title); + wv_title->enabled = NULL; + wv_title->button_type = BUTTON_TYPE_NONE; + wv_title->help = Qnil; + wv_title->next = wv_sep; + first_wv->contents = wv_title; + } + + pmenu = [[EmacsMenu alloc] initWithTitle: + [NSString stringWithUTF8String: SDATA (title)]]; + [pmenu fillWithWidgetValue: first_wv->contents]; + free_menubar_widget_value_tree (first_wv); + unbind_to (specpdl_count2, Qnil); + + tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps]; + [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow]; + + UNBLOCK_INPUT; + unbind_to (specpdl_count, Qnil); + UNGCPRO; + + if (error_name) error (error_name); + return tem; +} + + + + +/* ========================================================================== + + Toolbar: externally-called functions + + ========================================================================== */ + +void +free_frame_tool_bar (FRAME_PTR f) +/* -------------------------------------------------------------------------- + Under NS we just hide the toolbar until it might be needed again. + -------------------------------------------------------------------------- */ +{ + [[FRAME_NS_VIEW (f) toolbar] setVisible: NO]; +} + +void +update_frame_tool_bar (FRAME_PTR f) +/* -------------------------------------------------------------------------- + Update toolbar contents + -------------------------------------------------------------------------- */ +{ + int i; + EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar]; + + if (NILP (f->tool_bar_lines) || !INTEGERP (f->tool_bar_lines)) + return; + + [toolbar clearActive]; + + /* update EmacsToolbar as in GtkUtils, build items list */ + for (i = 0; i < f->n_tool_bar_items; ++i) + { +#define TOOLPROP(IDX) AREF (f->tool_bar_items, \ + i * TOOL_BAR_ITEM_NSLOTS + (IDX)) + + BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P)); + BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P)); + int idx; + int img_id; + struct image *img; + Lisp_Object image; + Lisp_Object helpObj; + char *helpText; + + /* If image is a vector, choose the image according to the + button state. */ + image = TOOLPROP (TOOL_BAR_ITEM_IMAGES); + if (VECTORP (image)) + { + /* NS toolbar auto-computes disabled and selected images */ + idx = TOOL_BAR_IMAGE_ENABLED_SELECTED; + xassert (ASIZE (image) >= idx); + image = AREF (image, idx); + } + else + { + idx = -1; + } + /* Ignore invalid image specifications. */ + if (!valid_image_p (image)) + { + NSLog (@"Invalid image for toolbar item"); + continue; + } + + img_id = lookup_image (f, image); + img = IMAGE_FROM_ID (f, img_id); + prepare_image_for_display (f, img); + + if (img->load_failed_p || img->pixmap == nil) + { + NSLog (@"Could not prepare toolbar image for display."); + continue; + } + + helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP); + if (NILP (helpObj)) + helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION); + helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj); + + [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText + enabled: enabled_p]; +#undef TOOLPROP + } + + if (![toolbar isVisible]) + [toolbar setVisible: YES]; + + if ([toolbar changed]) + { + /* inform app that toolbar has changed */ + NSDictionary *dict = [toolbar configurationDictionary]; + NSMutableDictionary *newDict = [dict mutableCopy]; + NSEnumerator *keys = [[dict allKeys] objectEnumerator]; + NSObject *key; + while ((key = [keys nextObject]) != nil) + { + NSObject *val = [dict objectForKey: key]; + if ([val isKindOfClass: [NSArray class]]) + { + [newDict setObject: + [toolbar toolbarDefaultItemIdentifiers: toolbar] + forKey: key]; + break; + } + } + [toolbar setConfigurationFromDictionary: newDict]; + [newDict release]; + } + +} + + +/* ========================================================================== + + Toolbar: class implementation + + ========================================================================== */ + +@implementation EmacsToolbar + +- initForView: (EmacsView *)view withIdentifier: (NSString *)identifier +{ + self = [super initWithIdentifier: identifier]; + emacsView = view; + [self setDisplayMode: NSToolbarDisplayModeIconOnly]; + [self setSizeMode: NSToolbarSizeModeSmall]; + [self setDelegate: self]; + identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10]; + activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8]; + prevEnablement = enablement = 0L; + return self; +} + +- (void)dealloc +{ + [prevIdentifiers release]; + [activeIdentifiers release]; + [identifierToItem release]; + [super dealloc]; +} + +- (void) clearActive +{ + [prevIdentifiers release]; + prevIdentifiers = [activeIdentifiers copy]; + [activeIdentifiers removeAllObjects]; + prevEnablement = enablement; + enablement = 0L; +} + +- (BOOL) changed +{ + return [activeIdentifiers isEqualToArray: prevIdentifiers] && + enablement == prevEnablement ? NO : YES; +} + +- (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx + helpText: (char *)help enabled: (BOOL)enabled +{ + /* 1) come up w/identifier */ + NSString *identifier = + [NSString stringWithFormat: @"%u", [img hash]]; + + /* 2) create / reuse item */ + NSToolbarItem *item = [identifierToItem objectForKey: identifier]; + if (item == nil) + { + item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier] + autorelease]; + [item setImage: img]; + [item setToolTip: [NSString stringWithCString: help]]; + [item setTarget: emacsView]; + [item setAction: @selector (toolbarClicked:)]; + } + + [item setTag: idx]; + [item setEnabled: enabled]; + + /* 3) update state */ + [identifierToItem setObject: item forKey: identifier]; + [activeIdentifiers addObject: identifier]; + enablement = (enablement << 1) | (enabled == YES); +} + +/* This overrides super's implementation, which automatically sets + all items to enabled state (for some reason). */ +- (void)validateVisibleItems { } + + +/* delegate methods */ + +- (NSToolbarItem *)toolbar: (NSToolbar *)toolbar + itemForItemIdentifier: (NSString *)itemIdentifier + willBeInsertedIntoToolbar: (BOOL)flag +{ + /* look up NSToolbarItem by identifier and return... */ + return [identifierToItem objectForKey: itemIdentifier]; +} + +- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar +{ + /* return entire set.. */ + return activeIdentifiers; +} + +/* for configuration palette (not yet supported) */ +- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar +{ + /* return entire set... */ + return [identifierToItem allKeys]; +} + +/* optional and unneeded */ +/* - toolbarWillAddItem: (NSNotification *)notification { } */ +/* - toolbarDidRemoveItem: (NSNotification *)notification { } */ +/* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */ + +@end /* EmacsToolbar */ + + + +/* ========================================================================== + + Tooltip: class implementation + + ========================================================================== */ + +/* Needed because NeXTstep does not provide enough control over tooltip + display. */ +@implementation EmacsTooltip + +- init +{ + NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0 + blue: 0.792 alpha: 0.95]; + NSFont *font = [NSFont toolTipsFontOfSize: 0]; + NSFont *sfont = [font screenFont]; + int height = [sfont ascender] - [sfont descender]; +/*[font boundingRectForFont].size.height; */ + NSRect r = NSMakeRect (0, 0, 100, height+6); + + textField = [[NSTextField alloc] initWithFrame: r]; + [textField setFont: font]; + [textField setBackgroundColor: col]; + + [textField setEditable: NO]; + [textField setSelectable: NO]; + [textField setBordered: YES]; + [textField setBezeled: YES]; + [textField setDrawsBackground: YES]; + + win = [[NSWindow alloc] + initWithContentRect: [textField frame] + styleMask: 0 + backing: NSBackingStoreBuffered + defer: YES]; + [win setReleasedWhenClosed: NO]; + [win setDelegate: self]; + [[win contentView] addSubview: textField]; +/* [win setBackgroundColor: col]; */ + [win setOpaque: NO]; + + return self; +} + +- (void) dealloc +{ + [win close]; + [win release]; + [textField release]; + [super dealloc]; +} + +- (void) setText: (char *)text +{ + NSString *str = [NSString stringWithUTF8String: text]; + NSRect r = [textField frame]; + r.size.width = [[[textField font] screenFont] widthOfString: str] + 8; + [textField setFrame: r]; + [textField setStringValue: str]; +} + +- (void) showAtX: (int)x Y: (int)y for: (int)seconds +{ + NSRect wr = [win frame]; + + wr.origin = NSMakePoint (x, y); + wr.size = [textField frame].size; + + [win setFrame: wr display: YES]; + [win orderFront: self]; + [win display]; + timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self + selector: @selector (hide) + userInfo: nil repeats: NO]; + [timer retain]; +} + +- (void) hide +{ + [win close]; + if (timer != nil) + { + if ([timer isValid]) + [timer invalidate]; + [timer release]; + timer = nil; + } +} + +- (BOOL) isActive +{ + return timer != nil; +} + +- (NSRect) frame +{ + return [textField frame]; +} + +@end /* EmacsTooltip */ + + + +/* ========================================================================== + + Popup Dialog: implementing functions + + ========================================================================== */ + +Lisp_Object +ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header) +{ + id dialog; + Lisp_Object window, tem; + struct frame *f; + NSPoint p; + BOOL isQ; + + NSTRACE (x-popup-dialog); + + check_ns (); + + isQ = NILP (header); + + if (EQ (position, Qt)) + { + window = selected_window; + } + else if (CONSP (position)) + { + Lisp_Object tem; + tem = Fcar (position); + if (XTYPE (tem) == Lisp_Cons) + window = Fcar (Fcdr (position)); + else + { + tem = Fcar (Fcdr (position)); /* EVENT_START (position) */ + window = Fcar (tem); /* POSN_WINDOW (tem) */ + } + } + else if (FRAMEP (position)) + { + window = position; + } + else + { + CHECK_LIVE_WINDOW (position); + window = position; + } + + if (FRAMEP (window)) + f = XFRAME (window); + else + { + CHECK_LIVE_WINDOW (window); + f = XFRAME (WINDOW_FRAME (XWINDOW (window))); + } + p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2; + p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2; + dialog = [[EmacsDialogPanel alloc] initFromContents: contents + isQuestion: isQ]; + + tem = [dialog runDialogAt: p]; + + [dialog close]; + + [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow]; + return tem; +} + + +/* ========================================================================== + + Popup Dialog: class implementation + + ========================================================================== */ + +@interface FlippedView : NSView +{ +} +@end + +@implementation FlippedView +- (BOOL)isFlipped +{ + return YES; +} +@end + +@implementation EmacsDialogPanel + +#define SPACER 8.0 +#define ICONSIZE 64.0 +#define TEXTHEIGHT 20.0 +#define MINCELLWIDTH 90.0 + +- initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle + backing: (NSBackingStoreType)backingType defer: (BOOL)flag +{ + NSSize spacing = {SPACER, SPACER}; + NSRect area; + char this_cmd_name[80]; + id cell, tem; + static NSImageView *imgView; + static FlippedView *contentView; + + if (imgView == nil) + { + NSImage *img; + area.origin.x = 3*SPACER; + area.origin.y = 2*SPACER; + area.size.width = ICONSIZE; + area.size.height= ICONSIZE; + img = [[NSImage imageNamed: @"NSApplicationIcon"] copy]; + [img setScalesWhenResized: YES]; + [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)]; + imgView = [[NSImageView alloc] initWithFrame: area]; + [imgView setImage: img]; + [imgView setEditable: NO]; + [img release]; + } + + aStyle = NSTitledWindowMask; + flag = YES; + rows = 0; + cols = 1; + [super initWithContentRect: contentRect styleMask: aStyle + backing: backingType defer: flag]; + contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]]; + [self setContentView: contentView]; + + [[self contentView] setAutoresizesSubviews: YES]; + + [[self contentView] addSubview: imgView]; + [self setTitle: @""]; + + area.origin.x += ICONSIZE+2*SPACER; +/* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */ + area.size.width = 400; + area.size.height= TEXTHEIGHT; + command = [[[NSTextField alloc] initWithFrame: area] autorelease]; + [[self contentView] addSubview: command]; + [command setStringValue: @"Emacs"]; + [command setDrawsBackground: NO]; + [command setBezeled: NO]; + [command setSelectable: NO]; + [command setFont: [NSFont boldSystemFontOfSize: 13.0]]; + +/* area.origin.x = ICONSIZE+2*SPACER; + area.origin.y = TEXTHEIGHT + 2*SPACER; + area.size.width = 400; + area.size.height= 2; + tem = [[[NSBox alloc] initWithFrame: area] autorelease]; + [[self contentView] addSubview: tem]; + [tem setTitlePosition: NSNoTitle]; + [tem setAutoresizingMask: NSViewWidthSizable];*/ + +/* area.origin.x = ICONSIZE+2*SPACER; */ + area.origin.y += TEXTHEIGHT+SPACER; + area.size.width = 400; + area.size.height= TEXTHEIGHT; + title = [[[NSTextField alloc] initWithFrame: area] autorelease]; + [[self contentView] addSubview: title]; + [title setDrawsBackground: NO]; + [title setBezeled: NO]; + [title setSelectable: NO]; + [title setFont: [NSFont systemFontOfSize: 11.0]]; + + cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease]; + [cell setBordered: NO]; + [cell setEnabled: NO]; + [cell setCellAttribute: NSCellIsInsetButton to: 8]; + [cell setBezelStyle: NSRoundedBezelStyle]; + + matrix = [[NSMatrix alloc] initWithFrame: contentRect + mode: NSHighlightModeMatrix + prototype: cell + numberOfRows: 0 + numberOfColumns: 1]; + [[self contentView] addSubview: matrix]; + [matrix release]; + [matrix setFrameOrigin: NSMakePoint (area.origin.x, + area.origin.y + (TEXTHEIGHT+3*SPACER))]; + [matrix setIntercellSpacing: spacing]; + + [self setOneShot: YES]; + [self setReleasedWhenClosed: YES]; + [self setHidesOnDeactivate: YES]; + return self; +} + + +- (BOOL)windowShouldClose: (id)sender +{ + [NSApp stopModalWithCode: Qnil]; + return NO; +} + + +void process_dialog (id window, Lisp_Object list) +{ + Lisp_Object item; + int row = 0; + + for (; XTYPE (list) == Lisp_Cons; list = XCDR (list)) + { + item = XCAR (list); + if (XTYPE (item) == Lisp_String) + { + [window addString: XSTRING (item)->data row: row++]; + } + else if (XTYPE (item) == Lisp_Cons) + { + [window addButton: XSTRING (XCAR (item))->data + value: XCDR (item) row: row++]; + } + else if (NILP (item)) + { + [window addSplit]; + row = 0; + } + } +} + + +- addButton: (char *)str value: (Lisp_Object)val row: (int)row +{ + id cell; + + if (row >= rows) + { + [matrix addRow]; + rows++; + } + cell = [matrix cellAtRow: row column: cols-1]; + [cell setTarget: self]; + [cell setAction: @selector (clicked: )]; + [cell setTitle: [NSString stringWithUTF8String: str]]; + [cell setTag: (int)val]; + [cell setBordered: YES]; + [cell setEnabled: YES]; + + return self; +} + + +- addString: (char *)str row: (int)row +{ + id cell; + + if (row >= rows) + { + [matrix addRow]; + rows++; + } + cell = [matrix cellAtRow: row column: cols-1]; + [cell setTitle: [NSString stringWithUTF8String: str]]; + [cell setBordered: YES]; + [cell setEnabled: NO]; + + return self; +} + + +- addSplit +{ + [matrix addColumn]; + cols++; + return self; +} + + +- clicked: sender +{ + NSArray *sellist = nil; + Lisp_Object seltag; + + sellist = [sender selectedCells]; + if ([sellist count]<1) + return self; + + seltag = (Lisp_Object)[[sellist objectAtIndex: 0] tag]; + if (! EQ (seltag, Qundefined)) + [NSApp stopModalWithCode: seltag]; + return self; +} + + +- initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ +{ + Lisp_Object head; + [super init]; + + if (XTYPE (contents) == Lisp_Cons) + { + head = Fcar (contents); + process_dialog (self, Fcdr (contents)); + } + else + head = contents; + + if (XTYPE (head) == Lisp_String) + [title setStringValue: + [NSString stringWithUTF8String: XSTRING (head)->data]]; + else if (isQ == YES) + [title setStringValue: @"Question"]; + else + [title setStringValue: @"Information"]; + + { + int i; + NSRect r, s, t; + + if (cols == 1 && rows > 1) /* Never told where to split */ + { + [matrix addColumn]; + for (i = 0; i<rows/2; i++) + { + [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0] + atRow: i column: 1]; + [matrix removeRow: (rows+1)/2]; + } + } + + [matrix sizeToFit]; + { + NSSize csize = [matrix cellSize]; + if (csize.width < MINCELLWIDTH) + { + csize.width = MINCELLWIDTH; + [matrix setCellSize: csize]; + [matrix sizeToCells]; + } + } + + [title sizeToFit]; + [command sizeToFit]; + + t = [matrix frame]; + r = [title frame]; + if (r.size.width+r.origin.x > t.size.width+t.origin.x) + { + t.origin.x = r.origin.x; + t.size.width = r.size.width; + } + r = [command frame]; + if (r.size.width+r.origin.x > t.size.width+t.origin.x) + { + t.origin.x = r.origin.x; + t.size.width = r.size.width; + } + + r = [self frame]; + s = [(NSView *)[self contentView] frame]; + r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width; + r.size.height += t.origin.y+t.size.height+SPACER-s.size.height; + [self setFrame: r display: NO]; + } + + return self; +} + + +- (void)dealloc +{ + { [super dealloc]; return; }; +} + + +- (Lisp_Object)runDialogAt: (NSPoint)p +{ + NSEvent *e; + NSModalSession session; + int ret; + + [self center]; /*XXX p ignored? */ + [self orderFront: NSApp]; + + session = [NSApp beginModalSessionForWindow: self]; + while ((ret = [NSApp runModalSession: session]) == NSRunContinuesResponse) + { + (e = [NSApp nextEventMatchingMask: NSAnyEventMask + untilDate: [NSDate distantFuture] + inMode: NSEventTrackingRunLoopMode + dequeue: NO]); +/*fprintf (stderr, "ret = %d\te = %p\n", ret, e); */ + } + [NSApp endModalSession: session]; + + return (Lisp_Object)ret; +} + +@end + + + +/* ========================================================================== + + Lisp definitions + + ========================================================================== */ + +DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0, + "Cause the NS menu to be re-calculated.") + () +{ + set_frame_menubar (SELECTED_FRAME (), 1, 0); + return Qnil; +} + + +DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0, + "Pop up a deck-of-cards menu and return user's selection.\n\ +POSITION is a position specification. This is either a mouse button event\n\ +or a list ((XOFFSET YOFFSET) WINDOW)\n\ +where XOFFSET and YOFFSET are positions in pixels from the top left\n\ +corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\ +This controls the position of the center of the first line\n\ +in the first pane of the menu, not the top left of the menu as a whole.\n\ +\n\ +MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\ +The menu items come from key bindings that have a menu string as well as\n\ +a definition; actually, the \"definition\" in such a key binding looks like\n\ +\(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\ +the keymap as a top-level element.\n\n\ +You can also use a list of keymaps as MENU.\n\ + Then each keymap makes a separate pane.\n\ +When MENU is a keymap or a list of keymaps, the return value\n\ +is a list of events.\n\n\ +Alternatively, you can specify a menu of multiple panes\n\ + with a list of the form (TITLE PANE1 PANE2...),\n\ +where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\ +Each ITEM is normally a cons cell (STRING . VALUE);\n\ +but a string can appear as an item--that makes a nonselectable line\n\ +in the menu.\n\ +With this form of menu, the return value is VALUE from the chosen item.") + (position, menu) + Lisp_Object position, menu; +{ + return ns_popup_menu (position, menu); +} + + +DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0, + doc: /* Pop up a dialog box and return user's selection. +POSITION specifies which frame to use. +This is normally a mouse button event or a window or frame. +If POSITION is t, it means to use the frame the mouse is on. +The dialog box appears in the middle of the specified frame. + +CONTENTS specifies the alternatives to display in the dialog box. +It is a list of the form (DIALOG ITEM1 ITEM2...). +Each ITEM is a cons cell (STRING . VALUE). +The return value is VALUE from the chosen item. + +An ITEM may also be just a string--that makes a nonselectable item. +An ITEM may also be nil--that means to put all preceding items +on the left of the dialog box and all following items on the right. +\(By default, approximately half appear on each side.) + +If HEADER is non-nil, the frame title for the box is "Information", +otherwise it is "Question". + +If the user gets rid of the dialog box without making a valid choice, +for instance using the window manager, then this produces a quit and +`x-popup-dialog' does not return. */) + (position, contents, header) + Lisp_Object position, contents, header; +{ + return ns_popup_dialog (position, contents, header); +} + + +/* ========================================================================== + + Lisp interface declaration + + ========================================================================== */ + +void +syms_of_nsmenu () +{ + defsubr (&Sx_popup_menu); + defsubr (&Sx_popup_dialog); + defsubr (&Sns_reset_menu); + staticpro (&menu_items); + menu_items = Qnil; + + Qdebug_on_next_call = intern ("debug-on-next-call"); + staticpro (&Qdebug_on_next_call); +} diff --git a/src/nsselect.m b/src/nsselect.m new file mode 100644 index 00000000000..a999fc38365 --- /dev/null +++ b/src/nsselect.m @@ -0,0 +1,624 @@ +/* NeXT/Open/GNUstep / MacOSX Cocoa selection processing for emacs. + Copyright (C) 1993, 1994, 2005, 2006, 2008, + Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +Originally by Carl Edman +Updated by Christian Limpach (chris@nice.ch) +OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com) +MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net) +GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu) + +*/ + +#include "config.h" +#include "lisp.h" +#include "nsterm.h" +#include "termhooks.h" + +#define CUT_BUFFER_SUPPORT + +Lisp_Object QPRIMARY, QSECONDARY, QTEXT, QFILE_NAME; + +static Lisp_Object Vns_sent_selection_hooks; +static Lisp_Object Vns_lost_selection_hooks; +static Lisp_Object Vselection_alist; +static Lisp_Object Vselection_converter_alist; + +/* 23: new */ +/* Coding system for communicating with other programs. */ +static Lisp_Object Vselection_coding_system; +/* Coding system for the next communicating with other programs. */ +static Lisp_Object Vnext_selection_coding_system; +static Lisp_Object Qforeign_selection; + +NSString *NXSecondaryPboard; + + + +/* ========================================================================== + + Internal utility functions + + ========================================================================== */ + + +static NSString * +symbol_to_nsstring (Lisp_Object sym) +{ + CHECK_SYMBOL (sym); + if (EQ (sym, QPRIMARY)) return NSGeneralPboard; + if (EQ (sym, QSECONDARY)) return NXSecondaryPboard; + if (EQ (sym, QTEXT)) return NSStringPboardType; + return [NSString stringWithUTF8String: XSTRING (XSYMBOL (sym)->xname)->data]; +} + + +static Lisp_Object +ns_string_to_symbol (NSString *t) +{ + if ([t isEqualToString: NSGeneralPboard]) + return QPRIMARY; + if ([t isEqualToString: NXSecondaryPboard]) + return QSECONDARY; + if ([t isEqualToString: NSStringPboardType]) + return QTEXT; + if ([t isEqualToString: NSFilenamesPboardType]) + return QFILE_NAME; + if ([t isEqualToString: NSTabularTextPboardType]) + return QTEXT; + return intern ([t UTF8String]); +} + + +static Lisp_Object +clean_local_selection_data (Lisp_Object obj) +{ + if (CONSP (obj) + && INTEGERP (XCAR (obj)) + && CONSP (XCDR (obj)) + && INTEGERP (XCAR (XCDR (obj))) + && NILP (XCDR (XCDR (obj)))) + obj = Fcons (XCAR (obj), XCDR (obj)); + + if (CONSP (obj) + && INTEGERP (XCAR (obj)) + && INTEGERP (XCDR (obj))) + { + if (XINT (XCAR (obj)) == 0) + return XCDR (obj); + if (XINT (XCAR (obj)) == -1) + return make_number (- XINT (XCDR (obj))); + } + + if (VECTORP (obj)) + { + int i; + int size = XVECTOR (obj)->size; + Lisp_Object copy; + + if (size == 1) + return clean_local_selection_data (XVECTOR (obj)->contents [0]); + copy = Fmake_vector (size, Qnil); + for (i = 0; i < size; i++) + XVECTOR (copy)->contents [i] + = clean_local_selection_data (XVECTOR (obj)->contents [i]); + return copy; + } + + return obj; +} + + +static void +ns_declare_pasteboard (id pb) +{ + [pb declareTypes: ns_send_types owner: NSApp]; +} + + +static void +ns_undeclare_pasteboard (id pb) +{ + [pb declareTypes: [NSArray array] owner: nil]; +} + + +static void +ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype) +{ + if (EQ (str, Qnil)) + { + [pb declareTypes: [NSArray array] owner: nil]; + } + else + { + char *utfStr; + NSString *type, *nsStr; + NSEnumerator *tenum; + + CHECK_STRING (str); + + utfStr = XSTRING (str)->data; + nsStr = [NSString stringWithUTF8String: utfStr]; + + if (gtype == nil) + { + [pb declareTypes: ns_send_types owner: nil]; + tenum = [ns_send_types objectEnumerator]; + while ( (type = [tenum nextObject]) ) + [pb setString: nsStr forType: type]; + } + else + { + [pb setString: nsStr forType: gtype]; + } + } +} + + +static Lisp_Object +ns_get_local_selection (Lisp_Object selection_name, + Lisp_Object target_type) +{ + Lisp_Object local_value; + Lisp_Object handler_fn, value, type, check; + int count; + + local_value = assq_no_quit (selection_name, Vselection_alist); + + if (NILP (local_value)) return Qnil; + + count = specpdl_ptr - specpdl; + specbind (Qinhibit_quit, Qt); + CHECK_SYMBOL (target_type); + handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist)); + if (!NILP (handler_fn)) + value =call3 (handler_fn, selection_name, target_type, + XCAR (XCDR (local_value))); + else + value =Qnil; + unbind_to (count, Qnil); + + check =value; + if (CONSP (value) && SYMBOLP (XCAR (value))) + { + type = XCAR (value); + check = XCDR (value); + } + + if (STRINGP (check) || VECTORP (check) || SYMBOLP (check) + || INTEGERP (check) || NILP (value)) + return value; + + if (CONSP (check) + && INTEGERP (XCAR (check)) + && (INTEGERP (XCDR (check))|| + (CONSP (XCDR (check)) + && INTEGERP (XCAR (XCDR (check))) + && NILP (XCDR (XCDR (check)))))) + return value; + + Fsignal (Qquit, Fcons (build_string ( + "invalid data returned by selection-conversion function"), + Fcons (handler_fn, Fcons (value, Qnil)))); +} + + +static Lisp_Object +ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target) +{ + id pb; + pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)]; + return ns_string_from_pasteboard (pb); +} + + +static void +ns_handle_selection_request (struct input_event *event) +{ + id pb =(id)event->x; + NSString *type =(NSString *)event->y; + Lisp_Object selection_name, selection_data, target_symbol, data; + Lisp_Object successful_p, rest; + + selection_name =ns_string_to_symbol ([(NSPasteboard *)pb name]); + target_symbol =ns_string_to_symbol (type); + selection_data = assq_no_quit (selection_name, Vselection_alist); + successful_p =Qnil; + + if (!NILP (selection_data)) + { + data = ns_get_local_selection (selection_name, target_symbol); + if (!NILP (data)) + { + if (STRINGP (data)) + ns_string_to_pasteboard_internal (pb, data, type); + successful_p =Qt; + } + } + + if (!EQ (Vns_sent_selection_hooks, Qunbound)) + { + for (rest =Vns_sent_selection_hooks;CONSP (rest); rest =Fcdr (rest)) + call3 (Fcar (rest), selection_name, target_symbol, successful_p); + } +} + + +static void +ns_handle_selection_clear (struct input_event *event) +{ + id pb = (id)event->x; + Lisp_Object selection_name, selection_data, rest; + + selection_name =ns_string_to_symbol ([(NSPasteboard *)pb name]); + selection_data =assq_no_quit (selection_name, Vselection_alist); + if (NILP (selection_data)) return; + + if (EQ (selection_data, Fcar (Vselection_alist))) + Vselection_alist = Fcdr (Vselection_alist); + else + { + for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest)) + if (EQ (selection_data, Fcar (Fcdr (rest)))) + Fsetcdr (rest, Fcdr (Fcdr (rest))); + } + + if (!EQ (Vns_lost_selection_hooks, Qunbound)) + { + for (rest =Vns_lost_selection_hooks;CONSP (rest); rest =Fcdr (rest)) + call1 (Fcar (rest), selection_name); + } +} + + + +/* ========================================================================== + + Functions used externally + + ========================================================================== */ + + +Lisp_Object +ns_string_from_pasteboard (id pb) +{ + NSString *type, *str; + const char *utfStr; + + type = [pb availableTypeFromArray: ns_return_types]; + if (type == nil) + { + Fsignal (Qquit, + Fcons (build_string ("empty or unsupported pasteboard type"), + Qnil)); + return Qnil; + } + + /* get the string */ + if (! (str = [pb stringForType: type])) + { + NSData *data = [pb dataForType: type]; + if (data != nil) + str = [[NSString alloc] initWithData: data + encoding: NSUTF8StringEncoding]; + if (str != nil) + { + [str autorelease]; + } + else + { + Fsignal (Qquit, + Fcons (build_string ("pasteboard doesn't contain valid data"), + Qnil)); + return Qnil; + } + } + + /* assume UTF8 */ + NS_DURING + { + /* EOL conversion: PENDING- is this too simple? */ + NSMutableString *mstr = [[str mutableCopy] autorelease]; + [mstr replaceOccurrencesOfString: @"\r\n" withString: @"\n" + options: NSLiteralSearch range: NSMakeRange (0, [mstr length])]; + [mstr replaceOccurrencesOfString: @"\r" withString: @"\n" + options: NSLiteralSearch range: NSMakeRange (0, [mstr length])]; + + utfStr = [mstr UTF8String]; + if (!utfStr) + utfStr = [mstr cString]; + } + NS_HANDLER + { + message1 ("ns_string_from_pasteboard: UTF8String failed\n"); + utfStr = [str lossyCString]; + } + NS_ENDHANDLER + + return build_string (utfStr); +} + + +void +ns_string_to_pasteboard (id pb, Lisp_Object str) +{ + ns_string_to_pasteboard_internal (pb, str, nil); +} + + + +/* ========================================================================== + + Lisp Defuns + + ========================================================================== */ + + +DEFUN ("ns-own-selection-internal", Fns_own_selection_internal, + Sns_own_selection_internal, 2, 2, 0, "Assert a selection.") + (selection_name, selection_value) + Lisp_Object selection_name, selection_value; +{ + id pb; + Lisp_Object old_value, new_value; + + check_ns (); + CHECK_SYMBOL (selection_name); + if (NILP (selection_value)) + error ("selection-value may not be nil."); + pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)]; + ns_declare_pasteboard (pb); + old_value =assq_no_quit (selection_name, Vselection_alist); + new_value = Fcons (selection_name, Fcons (selection_value, Qnil)); + if (NILP (old_value)) + Vselection_alist =Fcons (new_value, Vselection_alist); + else + Fsetcdr (old_value, Fcdr (new_value)); + /* XXX An evil hack, but a necessary one I fear XXX */ + { + struct input_event ev; + ev.kind = SELECTION_REQUEST_EVENT; + ev.modifiers = 0; + ev.code = 0; + ev.x = (int)pb; + ev.y = (int)NSStringPboardType; + ns_handle_selection_request (&ev); + } + return selection_value; +} + + +DEFUN ("ns-disown-selection-internal", Fns_disown_selection_internal, + Sns_disown_selection_internal, 1, 2, 0, + "If we own the selection SELECTION, disown it.") + (selection_name, time) + Lisp_Object selection_name, time; +{ + id pb; + check_ns (); + CHECK_SYMBOL (selection_name); + if (NILP (assq_no_quit (selection_name, Vselection_alist))) return Qnil; + + pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)]; + ns_undeclare_pasteboard (pb); + return Qt; +} + + +DEFUN ("ns-selection-exists-p", Fns_selection_exists_p, Sns_selection_exists_p, + 0, 1, 0, "Whether there is an owner for the given selection.\n\ +The arg should be the name of the selection in question, typically one of\n\ +the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\ +\(Those are literal upper-case symbol names.)\n\ +For convenience, the symbol nil is the same as `PRIMARY',\n\ +and t is the same as `SECONDARY'.)") + (selection) + Lisp_Object selection; +{ + id pb; + NSArray *types; + + check_ns (); + CHECK_SYMBOL (selection); + if (EQ (selection, Qnil)) selection = QPRIMARY; + if (EQ (selection, Qt)) selection = QSECONDARY; + pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection)]; + types =[pb types]; + return ([types count] == 0) ? Qnil : Qt; +} + + +DEFUN ("ns-selection-owner-p", Fns_selection_owner_p, Sns_selection_owner_p, + 0, 1, 0, + "Whether the current Emacs process owns the given selection.\n\ +The arg should be the name of the selection in question, typically one of\n\ +the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\ +\(Those are literal upper-case symbol names.)\n\ +For convenience, the symbol nil is the same as `PRIMARY',\n\ +and t is the same as `SECONDARY'.)") + (selection) + Lisp_Object selection; +{ + check_ns (); + CHECK_SYMBOL (selection); + if (EQ (selection, Qnil)) selection = QPRIMARY; + if (EQ (selection, Qt)) selection = QSECONDARY; + return (NILP (Fassq (selection, Vselection_alist))) ? Qnil : Qt; +} + + +DEFUN ("ns-get-selection-internal", Fns_get_selection_internal, + Sns_get_selection_internal, 2, 2, 0, + "Return text selected from some pasteboard.\n\ +SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\ +\(Those are literal upper-case symbol names.)\n\ +TYPE is the type of data desired, typically `STRING'.") + (selection_name, target_type) + Lisp_Object selection_name, target_type; +{ + Lisp_Object val; + + check_ns (); + CHECK_SYMBOL (selection_name); + CHECK_SYMBOL (target_type); + val = ns_get_local_selection (selection_name, target_type); + if (NILP (val)) + val = ns_get_foreign_selection (selection_name, target_type); + if (CONSP (val) && SYMBOLP (Fcar (val))) + { + val = Fcdr (val); + if (CONSP (val) && NILP (Fcdr (val))) + val = Fcar (val); + } + val = clean_local_selection_data (val); + return val; +} + + +#ifdef CUT_BUFFER_SUPPORT +DEFUN ("ns-get-cut-buffer-internal", Fns_get_cut_buffer_internal, + Sns_get_cut_buffer_internal, 1, 1, 0, + "Returns the value of the named cut buffer.") + (buffer) + Lisp_Object buffer; +{ + id pb; + check_ns (); + pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)]; + return ns_string_from_pasteboard (pb); +} + + +DEFUN ("ns-rotate-cut-buffers-internal", Fns_rotate_cut_buffers_internal, + Sns_rotate_cut_buffers_internal, 1, 1, 0, + "Rotate the values of the cut buffers by the given number of steps;\n\ + positive means move values forward, negative means backward. CURRENTLY NOT IMPLEMENTED UNDER NeXTstep.") + (n) + Lisp_Object n; +{ + /* XXX This function is unimplemented under NeXTstep XXX */ + Fsignal (Qquit, Fcons (build_string ( + "Warning: ns-rotate-cut-buffers-internal not implemented\n"), Qnil)); + return Qnil; +} + + +DEFUN ("ns-store-cut-buffer-internal", Fns_store_cut_buffer_internal, + Sns_store_cut_buffer_internal, 2, 2, 0, + "Sets the value of the named cut buffer (typically CUT_BUFFER0).") + (buffer, string) + Lisp_Object buffer, string; +{ + id pb; + check_ns (); + pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)]; + ns_string_to_pasteboard (pb, string); + return Qnil; +} +#endif + + +void +nxatoms_of_nsselect (void) +{ + NXSecondaryPboard = @"Selection"; +} + +void +syms_of_nsselect (void) +{ + QPRIMARY = intern ("PRIMARY"); staticpro (&QPRIMARY); + QSECONDARY = intern ("SECONDARY"); staticpro (&QSECONDARY); + QTEXT = intern ("TEXT"); staticpro (&QTEXT); + QFILE_NAME = intern ("FILE_NAME"); staticpro (&QFILE_NAME); + + defsubr (&Sns_disown_selection_internal); + defsubr (&Sns_get_selection_internal); + defsubr (&Sns_own_selection_internal); + defsubr (&Sns_selection_exists_p); + defsubr (&Sns_selection_owner_p); +#ifdef CUT_BUFFER_SUPPORT + defsubr (&Sns_get_cut_buffer_internal); + defsubr (&Sns_rotate_cut_buffers_internal); + defsubr (&Sns_store_cut_buffer_internal); +#endif + + Vselection_alist = Qnil; + staticpro (&Vselection_alist); + + DEFVAR_LISP ("ns-sent-selection-hooks", &Vns_sent_selection_hooks, + "A list of functions to be called when Emacs answers a selection request.\n\ +The functions are called with four arguments:\n\ + - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\ + - the selection-type which Emacs was asked to convert the\n\ + selection into before sending (for example, `STRING' or `LENGTH');\n\ + - a flag indicating success or failure for responding to the request.\n\ +We might have failed (and declined the request) for any number of reasons,\n\ +including being asked for a selection that we no longer own, or being asked\n\ +to convert into a type that we don't know about or that is inappropriate.\n\ +This hook doesn't let you change the behavior of Emacs's selection replies,\n\ +it merely informs you that they have happened."); + Vns_sent_selection_hooks = Qnil; + + DEFVAR_LISP ("selection-converter-alist", &Vselection_converter_alist, + "An alist associating X Windows selection-types with functions.\n\ +These functions are called to convert the selection, with three args:\n\ +the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\ +a desired type to which the selection should be converted;\n\ +and the local selection value (whatever was given to `x-own-selection').\n\ +\n\ +The function should return the value to send to the X server\n\ +\(typically a string). A return value of nil\n\ +means that the conversion could not be done.\n\ +A return value which is the symbol `NULL'\n\ +means that a side-effect was executed,\n\ +and there is no meaningful selection value."); + Vselection_converter_alist = Qnil; + + DEFVAR_LISP ("ns-lost-selection-hooks", &Vns_lost_selection_hooks, + "A list of functions to be called when Emacs loses an X selection.\n\ +\(This happens when some other X client makes its own selection\n\ +or when a Lisp program explicitly clears the selection.)\n\ +The functions are called with one argument, the selection type\n\ +\(a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD')."); + Vns_lost_selection_hooks = Qnil; + +/* 23: { */ + DEFVAR_LISP ("selection-coding-system", &Vselection_coding_system, + doc: /* Coding system for communicating with other programs. +When sending or receiving text via cut_buffer, selection, and clipboard, +the text is encoded or decoded by this coding system. +The default value is determined by the system script code. */); + Vselection_coding_system = Qnil; + + DEFVAR_LISP ("next-selection-coding-system", &Vnext_selection_coding_system, + doc: /* Coding system for the next communication with other programs. +Usually, `selection-coding-system' is used for communicating with +other programs. But, if this variable is set, it is used for the +next communication only. After the communication, this variable is +set to nil. */); + Vnext_selection_coding_system = Qnil; + + Qforeign_selection = intern ("foreign-selection"); + staticpro (&Qforeign_selection); +/* } */ + +} diff --git a/src/nsterm.h b/src/nsterm.h new file mode 100644 index 00000000000..26036a83a71 --- /dev/null +++ b/src/nsterm.h @@ -0,0 +1,827 @@ +/* Definitions and headers for communication with NeXT/Open/GNUstep API. + Copyright (C) 1989, 1993, 2005, 2008 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "dispextern.h" +#include "frame.h" +#include "character.h" +#include "font.h" + +#ifdef HAVE_NS + +#ifdef __OBJC__ + +/* ========================================================================== + + The Emacs application + + ========================================================================== */ + +/* We override sendEvent: as a means to stop/start the event loop */ +@interface EmacsApp : NSApplication +{ +} +- (void)sendEvent: (NSEvent *)theEvent; +- (void)showPreferencesWindow: (id)sender; +@end + + +/* ========================================================================== + + The main Emacs view + + ========================================================================== */ + +@class EmacsToolbar; + +@interface EmacsView : NSView <NSTextInput> + { + char *old_title; + BOOL windowClosing; + NSString *workingText; + BOOL processingCompose; +@public + struct frame *emacsframe; + int rows, cols; + int scrollbarsNeedingUpdate; + EmacsToolbar *toolbar; + } + +/* AppKit-side interface */ +- menuDown: sender; +- toolbarClicked: (id)item; +- toggleToolbar: (id)sender; +- (void)keyDown: (NSEvent *)theEvent; +- (void)mouseDown: (NSEvent *)theEvent; +- (void)mouseUp: (NSEvent *)theEvent; +- setMiniwindowImage: (BOOL)setMini; + +/* Emacs-side interface */ +- initFrameFromEmacs: (struct frame *) f; +- (void) setRows: (int) r andColumns: (int) c; +- (void) setWindowClosing: (BOOL)closing; +- (EmacsToolbar *) toolbar; +- (void) deleteWorkingText; +@end + + +/* Small utility used for processing resize events under Cocoa. */ +@interface EmacsWindow : NSWindow +{ + NSPoint grabOffset; +} +@end + + +/* ========================================================================== + + The main menu implementation + + ========================================================================== */ + +@interface EmacsMenu : NSMenu +{ + struct frame *frame; + unsigned long keyEquivModMask; +} + +- initWithTitle: (NSString *)title frame: (struct frame *)f; +- (void)setFrame: (struct frame *)f; +- (void)menuNeedsUpdate: (NSMenu *)menu; /* (delegate method) */ +- (NSString *)parseKeyEquiv: (char *)key; +- (id <NSMenuItem>)addItemWithWidgetValue: (void *)wvptr; +- (void)fillWithWidgetValue: (void *)wvptr; +- (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f; +- (void) clear; +- (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f + keymaps: (int)keymaps; +@end + + +/* ========================================================================== + + Toolbar + + ========================================================================== */ + +@class EmacsImage; + +@interface EmacsToolbar : NSToolbar + { + EmacsView *emacsView; + NSMutableDictionary *identifierToItem; + NSMutableArray *activeIdentifiers; + NSArray *prevIdentifiers; + unsigned long enablement, prevEnablement; + } +- initForView: (EmacsView *)view withIdentifier: (NSString *)identifier; +- (void) clearActive; +- (BOOL) changed; +- (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx + helpText: (char *)help + enabled: (BOOL)enabled; +/* delegate methods */ +- (NSToolbarItem *)toolbar: (NSToolbar *)toolbar + itemForItemIdentifier: (NSString *)itemIdentifier + willBeInsertedIntoToolbar: (BOOL)flag; +- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar; +- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar; +@end + + +/* ========================================================================== + + Message / question windows + + ========================================================================== */ + +@interface EmacsDialogPanel : NSPanel + { + NSTextField *command; + NSTextField *title; + NSMatrix *matrix; + int rows, cols; + } +- initFromContents: (Lisp_Object)menu isQuestion: (BOOL)isQ; +- addButton: (char *)str value: (Lisp_Object)val row: (int)row; +- addString: (char *)str row: (int)row; +- addSplit; +- (Lisp_Object)runDialogAt: (NSPoint)p; +@end + +@interface EmacsTooltip : NSObject + { + NSWindow *win; + NSTextField *textField; + NSTimer *timer; + } +- init; +- (void) setText: (char *)text; +- (void) showAtX: (int)x Y: (int)y for: (int)seconds; +- (void) hide; +- (BOOL) isActive; +- (NSRect) frame; +@end + + +/* ========================================================================== + + File open/save panels + This and next override methods to work around OS X behavior of + restarting application loop when user dismisses panel. + + ========================================================================== */ + +@interface EmacsSavePanel : NSSavePanel +{ +} +@end +@interface EmacsOpenPanel : NSOpenPanel +{ +} +@end + +@interface EmacsFileDelegate : NSObject +{ +} +- (BOOL)panel: (id)sender isValidFilename: (NSString *)filename; +- (BOOL)panel: (id)sender shouldShowFilename: (NSString *)filename; +- (NSString *)panel: (id)sender userEnteredFilename: (NSString *)filename + confirmed: (BOOL)okFlag; +@end + + +/* ========================================================================== + + Images and stippling + + ========================================================================== */ + +@interface EmacsImage : NSImage +{ + id imageListNext; + int refCount; + NSBitmapImageRep *bmRep; /* used for accessing pixel data */ + unsigned char *pixmapData[5]; /* shortcut to access pixel data */ + BOOL onTiger; + NSColor *stippleMask; +} ++ allocInitFromFile: (Lisp_Object)file; +- reference; +- imageListSetNext: (id)arg; +- imageListNext; +- (void)dealloc; +- initFromXBM: (unsigned char *)bits width: (int)w height: (int)h + flip: (BOOL)flip; +- initFromSkipXBM: (unsigned char *)bits width: (int)w height: (int)h + flip: (BOOL)flip length: (int)length; +- setXBMColor: (NSColor *)color; +- initForXPMWithDepth: (int)depth width: (int)width height: (int)height; +- (void)setPixmapData; +- (unsigned long)getPixelAtX: (int)x Y: (int)y; +- (void)setPixelAtX: (int)x Y: (int)y toRed: (unsigned char)r + green: (unsigned char)g blue: (unsigned char)b + alpha:(unsigned char)a; +- (void)setAlphaAtX: (int)x Y: (int)y to: (unsigned char)a; +- (NSColor *)stippleMask; +@end + + +/* ========================================================================== + + Scrollbars + + ========================================================================== */ + +@interface EmacsScroller : NSScroller + { + Lisp_Object win; + struct frame *frame; + NSResponder *prevResponder; + + /* offset to the bottom of knob of last mouse down */ + float last_mouse_offset; + float min_portion; + int pixel_height; + int last_hit_part; + + BOOL condemned; + + /* optimize against excessive positioning calls generated by emacs */ + int em_position; + int em_portion; + int em_whole; + } + +- initFrame: (NSRect )r window: (Lisp_Object)win; +- (void)setFrame: (NSRect)r; +- (void)dealloc; + +- setPosition: (int) position portion: (int) portion whole: (int) whole; +- (int) checkSamePosition: (int)position portion: (int)portion + whole: (int)whole; +- (void) getMouseMotionPart: (int *)part window: (Lisp_Object *)window + x: (Lisp_Object *)x y: ( Lisp_Object *)y; +- (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e; +- repeatScroll: (NSTimer *)sender; +- condemn; +- reprieve; +- judge; +@end + + +/* ========================================================================== + + Rendering on Panther and above + + ========================================================================== */ + +#ifdef NS_IMPL_COCOA +/* rendering util */ +@interface EmacsGlyphStorage : NSObject <NSGlyphStorage> +{ +@public + NSAttributedString *attrStr; + NSMutableDictionary *dict; + CGGlyph *cglyphs; + unsigned long maxChar, maxGlyph; + long i, len; +} +- initWithCapacity: (unsigned long) c; +- (void) setString: (NSString *)str font: (NSFont *)font; +@end +#endif /* NS_IMPL_COCOA */ + + +/* ========================================================================== + + Running the preferences window + + ========================================================================== */ + +@interface EmacsPrefsController : NSObject +{ + struct frame *frame; + IBOutlet NSWindow *prefsWindow; + IBOutlet NSPopUpButton *alternateModMenu; + IBOutlet NSPopUpButton *commandModMenu; +#ifdef NS_IMPL_COCOA + IBOutlet NSPopUpButton *controlModMenu; + IBOutlet NSPopUpButton *functionModMenu; +#endif + IBOutlet NSMatrix *cursorTypeMatrix; + IBOutlet NSSlider *cursorBlinkSlider; + IBOutlet NSSlider *expandSpaceSlider; +#ifdef NS_IMPL_COCOA + IBOutlet NSButton *smoothFontsCheck; + IBOutlet NSButton *useQuickdrawCheck; + IBOutlet NSButton *useSysHiliteCheck; + BOOL prevUseHighlightColor; +#endif + float prevExpandSpace; + float prevBlinkRate; +} +- (IBAction)cancel: (id)sender; +- (IBAction)ok: (id)sender; +- (IBAction)resetToDefaults: (id)sender; +- (IBAction)runHelp: (id)sender; +- (IBAction)setColors: (id)sender; +- (IBAction)setDefaultFont: (id)sender; + +- (void) showForFrame: (struct frame *)f; +- (void) setPanelFromValues; +- (void) setValuesFromPanel; +@end + +extern NSArray *ns_send_types, *ns_return_types; +extern EmacsMenu *mainMenu, *svcsMenu; + +/* Apple removed the declaration, but kept the implementation */ +#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4 +@interface NSApplication (EmacsApp) +- (void)setAppleMenu: (NSMenu *)menu; +@end +#endif + +#endif /* __OBJC__ */ + + + +/* ========================================================================== + + Non-OO stuff + + ========================================================================== */ + +enum ns_cursor_types +{ + no_highlight =0, + filled_box, + hollow_box, + underscore, + bar +}; + + +/* could use list to store these, but rest of emacs has a big infrastructure + for managing a table of bitmap "records" */ +struct ns_bitmap_record +{ +#ifdef __OBJC__ + EmacsImage *img; +#else + void *img; +#endif + char *file; + int refcount; + int height, width, depth; +}; + +/* this to map between emacs color indices and NSColor objects */ +struct ns_color_table +{ + unsigned int size; + unsigned int avail; +#ifdef __OBJC__ + NSColor **colors; + NSMutableSet *empty_indices; +#else + void **items; + void *availIndices; +#endif +}; +#define NS_COLOR_CAPACITY 256 + +#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b)) +#define ARGB_TO_ULONG(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) + +#define ALPHA_FROM_ULONG(color) ((color) >> 24) +#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff) +#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff) +#define BLUE_FROM_ULONG(color) ((color) & 0xff) + +/* Do not change `* 0x101' in the following lines to `<< 8'. If + changed, image masks in 1-bit depth will not work. */ +#define RED16_FROM_ULONG(color) (RED_FROM_ULONG(color) * 0x101) +#define GREEN16_FROM_ULONG(color) (GREEN_FROM_ULONG(color) * 0x101) +#define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101) + +/* this extends font backend font */ +struct nsfont_info +{ + struct font font; + + char *name; /* postscript name, uniquely identifies on NS systems */ + float width; /* this and following metrics stored as float rather than int */ + float height; + float underpos; + float underwidth; + float size; +#ifdef __OBJC__ + NSFont *nsfont; + /* cgfont and synthItal are used only on OS X 10.3+ */ +#if defined (NS_IMPL_COCOA) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3) + CGFontRef cgfont; +#else /* GNUstep or OS X < 10.3 */ + void *cgfont; +#endif +#else /* ! OBJC */ + void *nsfont; + void *cgfont; +#endif + char bold, ital; /* convenience flags */ + char synthItal; + float voffset; /* mean of ascender/descender offsets */ + XCharStruct max_bounds; /* 23 */ + /* we compute glyph codes and metrics on-demand in blocks of 256 indexed + by hibyte, lobyte */ + unsigned short **glyphs; /* map unicode index to glyph */ + struct font_metrics **metrics; +}; + + +/* init'd in ns_initialize_display_info () */ +struct ns_display_info +{ + /* Chain of all ns_display_info structures. */ + struct ns_display_info *next; + + /* The generic display parameters corresponding to this NS display. */ + struct terminal *terminal; + + /* This is a cons cell of the form (NAME . FONT-LIST-CACHE). + The same cons cell also appears in ns_display_name_list. */ + Lisp_Object name_list_element; + + /* The number of fonts loaded. */ + int n_fonts; + + /* Minimum width over all characters in all fonts in font_table. */ + int smallest_char_width; + + /* Minimum font height over all fonts in font_table. */ + int smallest_font_height; + + struct kboard *kboard; + + /*/23 */ + struct ns_bitmap_record *bitmaps; + int bitmaps_size; + int bitmaps_last; + + /* 23 */ + struct image_cache *image_cache; + + struct ns_color_table *color_table; + + /* 23: Dimensions and DPI resolution of this screen */ + int height, width; + double resx, resy; + + /* 23: Mask of things that cause the mouse to be grabbed */ + int grabbed; + + /* 23 */ + int n_planes; + + /* 23 */ + int color_p; + + /* 23 */ + Window root_window; + + /* 23: Xism */ + XrmDatabase xrdb; + + /* 23: The cursor to use for vertical scroll bars. */ + Cursor vertical_scroll_bar_cursor; + + /* 23: most mouse face stuff moved in here (and reasonably so) */ + int mouse_face_beg_row, mouse_face_beg_col; + int mouse_face_end_row, mouse_face_end_col; + int mouse_face_beg_x, mouse_face_beg_y; + int mouse_face_end_x, mouse_face_end_y; + int mouse_face_past_end; + Lisp_Object mouse_face_window; + int mouse_face_face_id; + int mouse_face_deferred_gc; + Lisp_Object mouse_face_overlay; + FRAME_PTR mouse_face_mouse_frame; + int mouse_face_mouse_x, mouse_face_mouse_y; + int mouse_face_defer; + int mouse_face_hidden; + int mouse_face_image_state; + + /* these are general, but we redefine due to Xism */ + struct frame *ns_highlight_frame; + struct frame *ns_focus_frame; +#define x_highlight_frame ns_highlight_frame +#define x_focus_frame ns_focus_frame +}; + +/* This is a chain of structures for all the NS displays currently in use. */ +extern struct ns_display_info *ns_display_list; +/* handle Xism */ +#define x_display_list ns_display_list + +extern Lisp_Object ns_display_name_list; +extern struct ns_display_info *ns_display_info_for_name (); + +/* 23: PENDING: these functions (we defined in nsfns) are used in various + places, but no prototypes are provided */ +struct ns_display_info *check_x_display_info (Lisp_Object frame); +FRAME_PTR check_x_frame (Lisp_Object frame); + + +struct ns_output +{ +#ifdef __OBJC__ + EmacsView *view; + id miniimage; + NSColor *current_cursor_color; + NSColor *desired_cursor_color; + NSColor *foreground_color; + NSColor *background_color; + EmacsToolbar *toolbar; +#else + void *view; + void *miniimage; + void *current_cursor_color; + void *desired_cursor_color; + void *foreground_color; + void *background_color; + void *toolbar; +#endif + + /* 23: NSCursors init'ed in initFrameFromEmacs */ + Cursor text_cursor; + Cursor nontext_cursor; + Cursor modeline_cursor; + Cursor hand_cursor; + Cursor hourglass_cursor; + Cursor horizontal_drag_cursor; + + /* 23: NS-specific */ + Cursor current_pointer; + + /* 23: lord knows why Emacs needs to know about our Window ids.. */ + Window window_desc, parent_desc; + char explicit_parent; + + struct font *font; + int baseline_offset; + + /* If a fontset is specified for this frame instead of font, this + value contains an ID of the fontset, else -1. */ + int fontset; /* only used with font_backend */ + + Lisp_Object icon_top; + Lisp_Object icon_left; + enum ns_cursor_types current_cursor, desired_cursor; + unsigned char last_inactive; + + /* The size of the extra width currently allotted for vertical + scroll bars, in pixels. */ + int vertical_scroll_bar_extra; + + /* The height of the titlebar decoration (included in NSWindow's frame). */ + int titlebar_height; + + /* The height of the toolbar if displayed, else 0. */ + int toolbar_height; + + /* This is the Emacs structure for the NS display this frame is on. */ + struct ns_display_info *display_info; +}; + +/* 23: this dummy decl now needed to support TTYs */ +struct x_output +{ + unsigned long background_pixel; + unsigned long foreground_pixel; +}; + + +/* This gives the ns_display_info structure for the display F is on. */ +#define FRAME_NS_DISPLAY_INFO(f) ((f)->output_data.ns->display_info) +/* the primacy of X must be constantly worked with... */ +#define FRAME_X_DISPLAY_INFO(f) ((f)->output_data.ns->display_info) +#define FRAME_X_OUTPUT(f) ((f)->output_data.ns) +#define FRAME_NS_WINDOW(f) ((f)->output_data.ns->window_desc) +#define FRAME_X_WINDOW(f) ((f)->output_data.ns->window_desc) + +/* This is the `Display *' which frame F is on. */ +#define FRAME_NS_DISPLAY(f) (0) +#define FRAME_X_DISPLAY(f) (0) + +#define FRAME_FOREGROUND_COLOR(f) ((f)->output_data.ns->foreground_color) +#define FRAME_BACKGROUND_COLOR(f) ((f)->output_data.ns->background_color) + +#define FRAME_X_IMAGE_CACHE(F) FRAME_NS_DISPLAY_INFO ((F))->image_cache + +#define NS_FACE_FOREGROUND(f) ((f)->foreground) +#define NS_FACE_BACKGROUND(f) ((f)->background) +#define FRAME_NS_TITLEBAR_HEIGHT(f) ((f)->output_data.ns->titlebar_height) +#define FRAME_NS_TOOLBAR_HEIGHT(f) ((f)->output_data.ns->toolbar_height) + +#define FONT_WIDTH(f) ((f)->max_width) +#define FONT_HEIGHT(f) ((f)->height) +/*#define FONT_BASE(f) ((f)->ascent) */ +#define FONT_BASE(f) (((struct nsfont_info *)f)->max_bounds.ascent) +/*#define FONT_DESCENT(f) ((f)->descent) */ +#define FONT_DESCENT(f) (((struct nsfont_info *)f)->max_bounds.descent) + +#define FRAME_DEFAULT_FACE(f) FACE_FROM_ID (f, DEFAULT_FACE_ID) + +#define FRAME_NS_VIEW(f) ((f)->output_data.ns->view) +#define FRAME_CURSOR(f) ((f)->output_data.ns->current_cursor) +#define FRAME_CURSOR_COLOR(f) ((f)->output_data.ns->current_cursor_color) +#define FRAME_NEW_CURSOR_COLOR(f) ((f)->output_data.ns->desired_cursor_color) +#define FRAME_NEW_CURSOR(f) ((f)->output_data.ns->desired_cursor) +#define FRAME_POINTER_TYPE(f) ((f)->output_data.ns->current_pointer) +#define FRAME_LAST_INACTIVE(f) ((f)->output_data.ns->last_inactive) + +#define FRAME_FONT(f) ((f)->output_data.ns->font) + +#ifdef __OBJC__ +#define XNS_SCROLL_BAR(vec) ((id) XSAVE_VALUE (vec)->pointer) +#else +#define XNS_SCROLL_BAR(vec) XSAVE_VALUE (vec)->pointer +#endif + +/* Compute pixel size for vertical scroll bars */ +#define NS_SCROLL_BAR_WIDTH(f) \ +(FRAME_HAS_VERTICAL_SCROLL_BARS (f) \ + ? rint (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0 \ + ? FRAME_CONFIG_SCROLL_BAR_WIDTH (f) \ + : (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f))) \ + : 0) + +/* Difference btwn char-column-calculated and actual SB widths. + This is only a concern for rendering when SB on left. */ +#define NS_SCROLL_BAR_ADJUST(w, f) \ +(WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (w) ? \ + (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f) \ + - NS_SCROLL_BAR_WIDTH (f)) : 0) + +/*PENDING: fix for GNUstep inconsistent accounting for titlebar */ +#ifdef NS_IMPL_GNUSTEP +#define NS_TOP_POS(f) ((f)->top_pos + 18) +#else +#define NS_TOP_POS(f) ((f)->top_pos) +#endif + +#define FRAME_NS_FONT_TABLE(f) (FRAME_NS_DISPLAY_INFO (f)->font_table) + +#define FRAME_FONTSET(f) ((f)->output_data.ns->fontset) + +/* 23 */ +#define FRAME_SMALLEST_CHAR_WIDTH(f) \ + (FRAME_NS_DISPLAY_INFO (f)->smallest_char_width) +#define FRAME_SMALLEST_FONT_HEIGHT(f) \ + (FRAME_NS_DISPLAY_INFO (f)->smallest_font_height) +#define FONT_TYPE_FOR_UNIBYTE(font, ch) 0 +#define FONT_TYPE_FOR_MULTIBYTE(font, ch) 0 +#define FRAME_BASELINE_OFFSET(f) ((f)->output_data.ns->baseline_offset) +#define BLACK_PIX_DEFAULT(f) 0x000000 +#define WHITE_PIX_DEFAULT(f) 0xFFFFFF + +/* First position where characters can be shown (instead of scrollbar, if + it is on left. */ +#define FIRST_CHAR_POSITION(f) \ + (! (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f)) ? 0 \ + : FRAME_SCROLL_BAR_COLS (f)) + +extern struct ns_display_info *ns_term_init (); +extern void ns_term_shutdown (int sig); + +/* constants for text rendering */ +#define NS_DUMPGLYPH_NORMAL 0 +#define NS_DUMPGLYPH_CURSOR 1 +#define NS_DUMPGLYPH_FOREGROUND 2 +#define NS_DUMPGLYPH_MOUSEFACE 3 + + +/* In nsfont, called from fontset.c */ +extern void nsfont_make_fontset_for_font (Lisp_Object name, + Lisp_Object font_object); + +/* In nsfont, for debugging */ +struct glyph_string; +void dump_glyphstring (struct glyph_string *s); + +/* Implemented in nsterm, published in or needed from nsfns. */ +extern Lisp_Object Qfontsize; +extern Lisp_Object ns_list_fonts (FRAME_PTR f, Lisp_Object pattern, + int size, int maxnames); +extern void ns_clear_frame (struct frame *f); + +#ifdef __OBJC__ +extern const char *ns_font_to_xlfd (NSFont *font); +#endif +extern const char *ns_fontname_to_xlfd (const char *name); +extern const char *ns_xlfd_to_fontname (const char *xlfd); + +extern void check_ns (void); +extern Lisp_Object ns_map_event_to_object (); +extern Lisp_Object ns_string_from_pasteboard (); +extern void ns_string_to_pasteboard (); +extern void nxatoms_of_nsselect (); +extern int ns_lisp_to_cursor_type (); +extern Lisp_Object ns_cursor_type_to_lisp (int arg); +extern Lisp_Object Qnone; + +/* XColor defined in dispextern.h (we use color_def->pixel = NSColor id), but + this causes an #include snafu, so we can't declare it. + extern int ns_defined_color (struct frame *f, char *name, XColor *color_def, + int alloc); */ + +#ifdef __OBJC__ +extern int ns_lisp_to_color (Lisp_Object color, NSColor **col); +extern NSColor *ns_lookup_indexed_color (unsigned long idx, struct frame *f); +extern unsigned long ns_index_color (NSColor *color, struct frame *f); +extern void ns_free_indexed_color (unsigned long idx, struct frame *f); +#endif + +/* C access to ObjC functionality */ +extern void ns_release_object (void *obj); +extern void ns_retain_object (void *obj); +extern void *ns_alloc_autorelease_pool (); +extern void ns_release_autorelease_pool (); + +/* in nsmenu */ +extern void update_frame_tool_bar (FRAME_PTR f); +extern void free_frame_tool_bar (FRAME_PTR f); +extern void find_and_call_menu_selection (FRAME_PTR f, + EMACS_INT menu_bar_items_used, Lisp_Object vector, void *client_data); +extern Lisp_Object find_and_return_menu_selection (FRAME_PTR f, + int keymaps, + void *client_data); +extern Lisp_Object ns_popup_dialog (Lisp_Object position, Lisp_Object contents, + Lisp_Object header); + +/* two more prototypes that should be moved to a more general include file */ +extern void set_frame_menubar (struct frame *f, int first_time, int deep_p); +extern void x_set_window_size (struct frame *f, int change_grav, + int cols, int rows); + +/* From nsimage.m, needed in image.c */ +struct image; +extern void *ns_image_from_XBM (unsigned char *bits, int width, int height); +extern void *ns_image_for_XPM (int width, int height, int depth); +extern void *ns_image_from_file (Lisp_Object file); +extern int ns_load_image (struct frame *f, struct image *img, + Lisp_Object spec_file, Lisp_Object spec_data); +extern int ns_image_width (void *img); +extern int ns_image_height (void *img); +extern unsigned long ns_get_pixel (void *img, int x, int y); +extern void ns_put_pixel (void *img, int x, int y, unsigned long argb); +extern void ns_set_alpha (void *img, int x, int y, unsigned char a); + +/* This in nsterm.m */ +extern unsigned long ns_get_rgb_color (struct frame *f, + float r, float g, float b, float a); +extern NSPoint last_mouse_motion_position; + +#ifdef NS_IMPL_GNUSTEP +extern char gnustep_base_version[]; /* version tracking */ +#endif + +#define MINWIDTH 10 +#define MINHEIGHT 10 + +/* Screen max coordinate + Using larger coordinates causes movewindow/placewindow to abort */ +#define SCREENMAX 16000 + +#define NS_SCROLL_BAR_WIDTH_DEFAULT [EmacsScroller scrollerWidth] +/* This is to match emacs on other platforms, ugly though it is. */ +#define NS_SELECTION_COLOR_DEFAULT @"LightGoldenrod2"; +#define RESIZE_HANDLE_SIZE 12 + +/* Little utility macros */ +#define IN_BOUND(min, x, max) (((x) < (min)) \ + ? (min) : (((x)>(max)) ? (max) : (x))) +#define SCREENMAXBOUND(x) (IN_BOUND (-SCREENMAX, x, SCREENMAX)) + +/* 23: needed somewhere... */ +#define VERTICAL_SCROLL_BAR_WIDTH_TRIM (0) + + +#endif /* HAVE_NS */ diff --git a/src/nsterm.m b/src/nsterm.m new file mode 100644 index 00000000000..fcf98749f09 --- /dev/null +++ b/src/nsterm.m @@ -0,0 +1,6598 @@ +/* NeXT/Open/GNUstep / MacOSX communication module. + Copyright (C) 1989, 1993, 1994, 2005, 2006, 2008, + Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +Originally by Carl Edman +Updated by Christian Limpach (chris@nice.ch) +OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com) +MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net) +GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu) +*/ + +#include <math.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include "config.h" +#include "lisp.h" +#include "blockinput.h" +#include "sysselect.h" +#include "nsterm.h" +#include "systime.h" +#include "character.h" +#include "fontset.h" +#include "composite.h" +#include "ccl.h" + +#include "termhooks.h" +#include "termopts.h" +#include "termchar.h" + +#include "window.h" +#include "keyboard.h" + +#include "font.h" + +/* call tracing */ +#if 0 +int term_trace_num = 0; +#define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \ + __FILE__, __LINE__, ++term_trace_num) +#else +#define NSTRACE(x) +#endif + + +/* ========================================================================== + + Local declarations + + ========================================================================== */ + +/* Special keycodes that we pass down the event chain */ +#define KEY_NS_POWER_OFF ((1<<28)|(0<<16)|1) +#define KEY_NS_OPEN_FILE ((1<<28)|(0<<16)|2) +#define KEY_NS_OPEN_TEMP_FILE ((1<<28)|(0<<16)|3) +#define KEY_NS_DRAG_FILE ((1<<28)|(0<<16)|4) +#define KEY_NS_DRAG_COLOR ((1<<28)|(0<<16)|5) +#define KEY_NS_DRAG_TEXT ((1<<28)|(0<<16)|6) +#define KEY_NS_CHANGE_FONT ((1<<28)|(0<<16)|7) +#define KEY_NS_OPEN_FILE_LINE ((1<<28)|(0<<16)|8) +#define KEY_NS_INSERT_WORKING_TEXT ((1<<28)|(0<<16)|9) +#define KEY_NS_DELETE_WORKING_TEXT ((1<<28)|(0<<16)|10) +#define KEY_NS_SPI_SERVICE_CALL ((1<<28)|(0<<16)|11) + +/* Convert a symbol indexed with an NSxxx value to a value as defined + in keyboard.c (lispy_function_key). I hope this is a correct way + of doing things... */ +static unsigned convert_ns_to_X_keysym[] = +{ + NSHomeFunctionKey, 0x50, + NSLeftArrowFunctionKey, 0x51, + NSUpArrowFunctionKey, 0x52, + NSRightArrowFunctionKey, 0x53, + NSDownArrowFunctionKey, 0x54, + NSPageUpFunctionKey, 0x55, + NSPageDownFunctionKey, 0x56, + NSEndFunctionKey, 0x57, + NSBeginFunctionKey, 0x58, + NSSelectFunctionKey, 0x60, + NSPrintFunctionKey, 0x61, + NSExecuteFunctionKey, 0x62, + NSInsertFunctionKey, 0x63, + NSUndoFunctionKey, 0x65, + NSRedoFunctionKey, 0x66, + NSMenuFunctionKey, 0x67, + NSFindFunctionKey, 0x68, + NSHelpFunctionKey, 0x6A, + NSBreakFunctionKey, 0x6B, + + NSF1FunctionKey, 0xBE, + NSF2FunctionKey, 0xBF, + NSF3FunctionKey, 0xC0, + NSF4FunctionKey, 0xC1, + NSF5FunctionKey, 0xC2, + NSF6FunctionKey, 0xC3, + NSF7FunctionKey, 0xC4, + NSF8FunctionKey, 0xC5, + NSF9FunctionKey, 0xC6, + NSF10FunctionKey, 0xC7, + NSF11FunctionKey, 0xC8, + NSF12FunctionKey, 0xC9, + NSF13FunctionKey, 0xCA, + NSF14FunctionKey, 0xCB, + NSF15FunctionKey, 0xCC, + + NSBackspaceCharacter, 0x08, /* 8: Not on some KBs. */ + NSDeleteCharacter, 0xFF, /* 127: Big 'delete' key upper right. */ + NSDeleteFunctionKey, 0x9F, /* 63272: Del forw key off main array. */ + + NSTabCharacter, 0x09, + 0x19, 0x09, /* left tab->regular since pass shift */ + NSCarriageReturnCharacter, 0x0D, + NSNewlineCharacter, 0x0D, + NSEnterCharacter, 0x8D, + + 0x1B, 0x1B /* escape */ +}; + + +/* Lisp communications */ +Lisp_Object ns_input_file, ns_input_font, ns_input_fontsize, ns_input_line; +Lisp_Object ns_input_color, ns_input_text, ns_working_text; +Lisp_Object ns_input_spi_name, ns_input_spi_arg; +Lisp_Object Vx_toolkit_scroll_bars; +static Lisp_Object Qmodifier_value; +/*PENDING: unsure why these defined in term files, anyway we need in keymap.c */ +Lisp_Object Qalt, Qcontrol, Qhyper, Qmeta, Qsuper; +extern Lisp_Object Qcursor_color, Qcursor_type, Qns; +extern int lisp_to_mod (Lisp_Object lmod); + + +EmacsPrefsController *prefsController; + +/* Defaults managed through the OpenStep defaults system. These pertain to + the NS interface specifically. Although a customization group could be + created, it's more natural to manage them via defaults. */ + +/* Specifies which emacs modifier should be generated when NS receives + the Alternate modifer. May be Qnone or any of the modifier lisp symbols. */ +Lisp_Object ns_alternate_modifier; + +/* Specifies which emacs modifier should be generated when NS receives + the Command modifer. May be any of the modifier lisp symbols. */ +Lisp_Object ns_command_modifier; + +/* Specifies which emacs modifier should be generated when NS receives + the Control modifer. May be any of the modifier lisp symbols. */ +Lisp_Object ns_control_modifier; + +/* Specifies which emacs modifier should be generated when NS receives + the Function modifer (laptops). May be any of the modifier lisp symbols. */ +Lisp_Object ns_function_modifier; + +/* A floating point value specifying the rate at which to blink the cursor. + YES indicates 0.5, NO indicates no blinking. */ +Lisp_Object ns_cursor_blink_rate; + +/* Used for liason with core emacs cursor-blink-mode. */ +Lisp_Object ns_cursor_blink_mode; + +/* A floating point value specifying vertical stretch (positive) or shrink + (negative) of text line spacing. Zero means default spacing. + YES indicates 0.5, NO indicates 0.0. */ +Lisp_Object ns_expand_space; + +/* Control via default 'GSFontAntiAlias' on OS X and GNUstep. */ +int ns_antialias_text; + +/* On OS X picks up the default NSGlobalDomain AppleAntiAliasingThreshold, + the maximum font size to NOT antialias. On GNUstep there is currently + no way to control this behavior. */ +float ns_antialias_threshold; + +/* Controls use of an undocumented CG function to do Quickdraw-style font + smoothing (less heavy) instead of regular Quartz smoothing. */ +int ns_use_qd_smoothing; + +/* Used to pick up AppleHighlightColor on OS X */ +int ns_use_system_highlight_color; +NSString *ns_selection_color; + + +NSArray *ns_send_types =0, *ns_return_types =0, *ns_drag_types =0; + +/* Display variables */ +struct ns_display_info *ns_display_list; /* Chain of existing displays */ +Lisp_Object ns_display_name_list; +long context_menu_value = 0; + +/* display update */ +NSPoint last_mouse_motion_position; +static NSRect last_mouse_glyph; +static unsigned long last_mouse_movement_time = 0; +static Lisp_Object last_mouse_motion_frame; +static EmacsScroller *last_mouse_scroll_bar = nil; +static struct frame *ns_updating_frame; +static NSView *focus_view = NULL; +static int ns_window_num =0; +static NSRect uRect; +static BOOL gsaved = NO; +BOOL ns_in_resize = NO; +int ns_tmp_flags; /*PENDING */ +struct nsfont_info *ns_tmp_font; /*PENDING */ +/*static int debug_lock = 0; */ + +#ifdef NS_IMPL_COCOA +/* This undocumented Quartz function controls how fonts are anti-aliased. + (Found from code in Mac wxWindows impl, discovered by running `nm' on + the "QD" framework.) + Mode 0 is normal anti-aliasing, mode 1 is no anti-aliasing, and mode 2 is + 4-bit pixel-aligned anti-aliasing (the old QuickDraw standard). */ +extern void CGContextSetFontRenderingMode (CGContextRef cg, int v); +#endif + + +/* event loop */ +static BOOL send_appdefined = YES; +static NSEvent *last_appdefined_event = 0; +static NSTimer *timed_entry = 0; +static NSTimer *fd_entry = nil; +static NSTimer *cursor_blink_entry = nil; +static NSTimer *scroll_repeat_entry = nil; +static fd_set select_readfds, t_readfds; +static struct timeval select_timeout; +static int select_nfds; +static NSAutoreleasePool *outerpool; +static BOOL ns_shutdown_properly = NO; +static struct input_event *emacs_event = NULL; +static struct input_event *q_event_ptr = NULL; +static int n_emacs_events_pending = 0; +static NSMutableArray *ns_pending_files, *ns_pending_service_names, + *ns_pending_service_args; +static BOOL inNsSelect = 0; + +/* Convert modifiers in a NeXTSTEP event to emacs style modifiers. */ +#define NS_FUNCTION_KEY_MASK 0x800000 +#define EV_MODIFIERS(e) \ + ((([e modifierFlags] & NSHelpKeyMask) ? \ + hyper_modifier : 0) \ + | (([e modifierFlags] & NSAlternateKeyMask) ? \ + lisp_to_mod (ns_alternate_modifier) : 0) \ + | (([e modifierFlags] & NSShiftKeyMask) ? \ + shift_modifier : 0) \ + | (([e modifierFlags] & NSControlKeyMask) ? \ + lisp_to_mod (ns_control_modifier) : 0) \ + | (([e modifierFlags] & NS_FUNCTION_KEY_MASK) ? \ + lisp_to_mod (ns_function_modifier) : 0) \ + | (([e modifierFlags] & NSCommandKeyMask) ? \ + lisp_to_mod (ns_command_modifier):0)) + +#define EV_UDMODIFIERS(e) \ + ((([e type] == NSLeftMouseDown) ? down_modifier : 0) \ + | (([e type] == NSRightMouseDown) ? down_modifier : 0) \ + | (([e type] == NSLeftMouseDragged) ? down_modifier : 0) \ + | (([e type] == NSRightMouseDragged) ? down_modifier : 0) \ + | (([e type] == NSLeftMouseUp) ? up_modifier : 0) \ + | (([e type] == NSRightMouseUp) ? up_modifier : 0)) + +#define EV_BUTTON(e) \ + ((([e type] == NSLeftMouseDown) || ([e type] == NSLeftMouseUp)) ? 0 : \ + (([e type] == NSRightMouseDown) || ([e type] == NSRightMouseUp)) ? 2 : 1) + +/* Convert the time field to a timestamp in milliseconds. */ +#ifdef NS_IMPL_GNUSTEP +/* Apple says timestamp is in seconds, but GNUstep seems to be returning msec */ +#define EV_TIMESTAMP(e) ([e timestamp]) +#else +#define EV_TIMESTAMP(e) ([e timestamp] * 1000) +#endif /* not gnustep */ + +/* This is a piece of code which is common to all the event handling + methods. Maybe it should even be a function. */ +#define EV_TRAILER(e) \ + { \ + XSETFRAME (emacs_event->frame_or_window, [NSApp isActive] ? \ + emacsframe : SELECTED_FRAME ()); \ + if (e) emacs_event->timestamp = EV_TIMESTAMP (e); \ + n_emacs_events_pending++; \ + kbd_buffer_store_event_hold (emacs_event, q_event_ptr); \ + EVENT_INIT (*emacs_event); \ + ns_send_appdefined (-1); \ + } + +/*PENDING: get rid of need for these forward declarations */ +static void ns_condemn_scroll_bars (struct frame *f), + ns_judge_scroll_bars (struct frame *f); + +/* unused variables needed for compatibility reasons */ +int x_use_underline_position_properties, x_underline_at_descent_line; +/* PENDING: figure out what to do with underline_minimum_offset. */ + + +/* ========================================================================== + + Utilities + + ========================================================================== */ + + +static Lisp_Object +append2 (Lisp_Object list, Lisp_Object item) +/* -------------------------------------------------------------------------- + Utility to append to a list + -------------------------------------------------------------------------- */ +{ + Lisp_Object array[2]; + array[0] = list; + array[1] = Fcons (item, Qnil); + return Fnconc (2, &array[0]); +} + + +void +ns_init_paths () +/* -------------------------------------------------------------------------- + Used to allow emacs to find its resources under Emacs.app + Called from emacs.c at startup. + -------------------------------------------------------------------------- */ +{ + NSBundle *bundle = [NSBundle mainBundle]; + NSString *binDir = [bundle bundlePath], *resourceDir = [bundle resourcePath]; + NSString *resourcePath, *resourcePaths; + NSRange range; + BOOL onWindows = NO; /* how do I determine this? */ + NSString *pathSeparator = onWindows ? @";" : @":"; + NSFileManager *fileManager = [NSFileManager defaultManager]; + BOOL isDir; +/*NSLog (@"ns_init_paths: '%@'\n%@\n", [[NSBundle mainBundle] bundlePath], [[NSBundle mainBundle] resourcePath]); */ + + /* get bindir from base */ + range = [resourceDir rangeOfString: @"Contents"]; + if (range.location != NSNotFound) + { + binDir = [binDir stringByAppendingPathComponent: @"Contents"]; +#ifdef NS_IMPL_COCOA + binDir = [binDir stringByAppendingPathComponent: @"MacOS"]; +#endif + } + + /* the following based on Andrew Choi's init_mac_osx_environment () */ + if (!getenv ("EMACSLOADPATH")) + { + NSArray *paths = [resourceDir stringsByAppendingPaths: + [NSArray arrayWithObjects: + @"site-lisp", @"lisp", @"leim", nil]]; + NSEnumerator *pathEnum = [paths objectEnumerator]; + resourcePaths = @""; + while (resourcePath = [pathEnum nextObject]) + { + if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir]) + if (isDir) + { + if ([resourcePaths length] > 0) + resourcePaths = + [resourcePaths stringByAppendingString: pathSeparator]; + resourcePaths = + [resourcePaths stringByAppendingString: resourcePath]; + } + } + if ([resourcePaths length] > 0) + setenv ("EMACSLOADPATH", [resourcePaths UTF8String], 1); +/*NSLog (@"loadPath: '%s'\n", resourcePaths); */ + } + + if (!getenv ("EMACSPATH")) + { + NSArray *paths = [binDir stringsByAppendingPaths: + [NSArray arrayWithObjects: @"bin", + @"lib-exec", nil]]; + NSEnumerator *pathEnum = [paths objectEnumerator]; + resourcePaths = @""; + while (resourcePath = [pathEnum nextObject]) + { + if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir]) + if (isDir) + { + if ([resourcePaths length] > 0) + resourcePaths = + [resourcePaths stringByAppendingString: pathSeparator]; + resourcePaths = + [resourcePaths stringByAppendingString: resourcePath]; + } + } + if ([resourcePaths length] > 0) + setenv ("EMACSPATH", [resourcePaths UTF8String], 1); + } + + resourcePath = [resourceDir stringByAppendingPathComponent: @"etc"]; + if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir]) + { + if (isDir) + { + if (!getenv ("EMACSDATA")) + setenv ("EMACSDATA", [resourcePath UTF8String], 1); + if (!getenv ("EMACSDOC")) + setenv ("EMACSDOC", [resourcePath UTF8String], 1); + } + } + + /*PENDING: append to INFOPATH... */ + if (!getenv ("INFOPATH")) + { + resourcePath = [resourceDir stringByAppendingPathComponent: @"info"]; + if ([fileManager fileExistsAtPath: resourcePath isDirectory: &isDir]) + if (isDir) + setenv ("INFOPATH", [resourcePath UTF8String], 1); + } +} + + +static int +timeval_subtract (struct timeval *result, struct timeval x, struct timeval y) +/* -------------------------------------------------------------------------- + Subtract the `struct timeval' values X and Y, storing the result in RESULT. + Return 1 if the difference is negative, otherwise 0. + -------------------------------------------------------------------------- */ +{ + /* Perform the carry for the later subtraction by updating y. + This is safer because on some systems + the tv_sec member is unsigned. */ + if (x.tv_usec < y.tv_usec) + { + int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1; + y.tv_usec -= 1000000 * nsec; + y.tv_sec += nsec; + } + if (x.tv_usec - y.tv_usec > 1000000) + { + int nsec = (y.tv_usec - x.tv_usec) / 1000000; + y.tv_usec += 1000000 * nsec; + y.tv_sec -= nsec; + } + + /* Compute the time remaining to wait. tv_usec is certainly positive. */ + result->tv_sec = x.tv_sec - y.tv_sec; + result->tv_usec = x.tv_usec - y.tv_usec; + + /* Return indication of whether the result should be considered negative. */ + return x.tv_sec < y.tv_sec; +} + +static void +ns_timeout (int usecs) +/* -------------------------------------------------------------------------- + Blocking timer utility used by ns_ring_bell + -------------------------------------------------------------------------- */ +{ + struct timeval wakeup; + + EMACS_GET_TIME (wakeup); + + /* Compute time to wait until, propagating carry from usecs. */ + wakeup.tv_usec += usecs; + wakeup.tv_sec += (wakeup.tv_usec / 1000000); + wakeup.tv_usec %= 1000000; + + /* Keep waiting until past the time wakeup. */ + while (1) + { + struct timeval timeout; + + EMACS_GET_TIME (timeout); + + /* In effect, timeout = wakeup - timeout. + Break if result would be negative. */ + if (timeval_subtract (&timeout, wakeup, timeout)) + break; + + /* Try to wait that long--but we might wake up sooner. */ + select (0, NULL, NULL, NULL, &timeout); + } +} + + +void +ns_release_object (void *obj) +/* -------------------------------------------------------------------------- + Release an object (callable from C) + -------------------------------------------------------------------------- */ +{ + [(id)obj release]; +} + + +void +ns_retain_object (void *obj) +/* -------------------------------------------------------------------------- + Retain an object (callable from C) + -------------------------------------------------------------------------- */ +{ + [(id)obj retain]; +} + + +void * +ns_alloc_autorelease_pool () +/* -------------------------------------------------------------------------- + Allocate a pool for temporary objects (callable from C) + -------------------------------------------------------------------------- */ +{ + return [[NSAutoreleasePool alloc] init]; +} + + +void +ns_release_autorelease_pool (void *pool) +/* -------------------------------------------------------------------------- + Free a pool and temporary objects it refers to (callable from C) + -------------------------------------------------------------------------- */ +{ + ns_release_object (pool); +} + + + +/* ========================================================================== + + Focus (clipping) and screen update + + ========================================================================== */ + +static NSRect +ns_resize_handle_rect (NSWindow *window) +{ + NSRect r = [window frame]; + r.origin.x = r.size.width - RESIZE_HANDLE_SIZE; + r.origin.y = 0; + r.size.width = r.size.height = RESIZE_HANDLE_SIZE; + return r; +} + + +static void +ns_update_begin (struct frame *f) +/* -------------------------------------------------------------------------- + Prepare for a grouped sequence of drawing calls + 23: external (RIF) call; now split w/ and called before update_window_begin + -------------------------------------------------------------------------- */ +{ + NSView *view = FRAME_NS_VIEW (f); + NSTRACE (ns_update_begin); +/*fprintf (stderr, "\\%p\n", f); */ + + ns_updating_frame = f; + [view lockFocus]; + +#ifdef NS_IMPL_GNUSTEP + uRect = NSMakeRect (0, 0, 0, 0); +#endif +} + + +static void +ns_update_window_begin (struct window *w) +/* -------------------------------------------------------------------------- + Prepare for a grouped sequence of drawing calls + 23: external (RIF) call; now split with and called after update_begin + -------------------------------------------------------------------------- */ +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (f); + NSTRACE (ns_update_window_begin); + + updated_window = w; + set_output_cursor (&w->cursor); + + BLOCK_INPUT; + + if (f == dpyinfo->mouse_face_mouse_frame) + { + /* Don't do highlighting for mouse motion during the update. */ + dpyinfo->mouse_face_defer = 1; + + /* If the frame needs to be redrawn, + simply forget about any prior mouse highlighting. */ + if (FRAME_GARBAGED_P (f)) + dpyinfo->mouse_face_window = Qnil; + + /* (further code for mouse faces ifdef'd out in other terms elided) */ + } + + UNBLOCK_INPUT; +} + + +static void +ns_update_window_end (struct window *w, int cursor_on_p, + int mouse_face_overwritten_p) +/* -------------------------------------------------------------------------- + Finished a grouped sequence of drawing calls + 23: external (RIF) call; now split with and called before update_window_end + -------------------------------------------------------------------------- */ +{ + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (XFRAME (w->frame)); + + /* note: this fn is nearly identical in all terms */ + if (!w->pseudo_window_p) + { + BLOCK_INPUT; + + if (cursor_on_p) + display_and_set_cursor (w, 1, + output_cursor.hpos, output_cursor.vpos, + output_cursor.x, output_cursor.y); + + if (draw_window_fringes (w, 1)) + x_draw_vertical_border (w); + + UNBLOCK_INPUT; + } + + /* If a row with mouse-face was overwritten, arrange for + frame_up_to_date to redisplay the mouse highlight. */ + if (mouse_face_overwritten_p) + { + dpyinfo->mouse_face_beg_row = dpyinfo->mouse_face_beg_col = -1; + dpyinfo->mouse_face_end_row = dpyinfo->mouse_face_end_col = -1; + dpyinfo->mouse_face_window = Qnil; + } + + updated_window = NULL; + NSTRACE (update_window_end); +} + + +static void +ns_update_end (struct frame *f) +/* -------------------------------------------------------------------------- + Finished a grouped sequence of drawing calls + 23: external (RIF) call; now split with and called after update_window_end + -------------------------------------------------------------------------- */ +{ + NSView *view = FRAME_NS_VIEW (f); + +/* if (f == FRAME_NS_DISPLAY_INFO (f)->mouse_face_mouse_frame) */ + FRAME_NS_DISPLAY_INFO (f)->mouse_face_defer = 0; + + BLOCK_INPUT; + +#ifdef NS_IMPL_GNUSTEP + /* trigger flush only in the rectangle we tracked as being drawn */ + [view unlockFocusNeedsFlush: NO]; +/*fprintf (stderr, " (%.0f, %.0f : %.0f x %.0f)", uRect.origin.x, uRect.origin.y, uRect.size.width, uRect.size.height); */ + [view lockFocusInRect: uRect]; +#endif + + [view unlockFocus]; + [[view window] flushWindow]; + + UNBLOCK_INPUT; + ns_updating_frame = NULL; + NSTRACE (ns_update_end); +} + + +static void +ns_flush (struct frame *f) +/* -------------------------------------------------------------------------- + 23: external (RIF) call + NS impl is no-op since currently we flush in ns_update_end and elsewhere + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_flush); +} + + +static void +ns_focus (struct frame *f, NSRect *r, int n) +/* -------------------------------------------------------------------------- + Internal: Focus on given frame. During small local updates this is used to + draw, however during large updates, ns_update_begin and ns_update_end are + called to wrap the whole thing, in which case these calls are stubbed out. + Except, on GNUstep, we accumulate the rectangle being drawn into, because + the back end won't do this automatically, and will just end up flushing + the entire window. + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_focus); +#ifdef NS_IMPL_GNUSTEP + NSRect u; + if (n == 2) + u = NSUnionRect (r[0], r[1]); + else if (r) + u = *r; +#endif +/* static int c =0; + fprintf (stderr, "focus: %d", c++); + if (r) fprintf (stderr, " (%.0f, %.0f : %.0f x %.0f)", r->origin.x, r->origin.y, r->size.width, r->size.height); + fprintf (stderr, "\n"); */ + + if (f != ns_updating_frame) + { + NSView *view = FRAME_NS_VIEW (f); + if (view != focus_view) + { + if (focus_view != NULL) + { + [focus_view unlockFocus]; + [[focus_view window] flushWindow]; +/*debug_lock--; */ + } + + if (view) +#ifdef NS_IMPL_GNUSTEP + r ? [view lockFocusInRect: u] : [view lockFocus]; +#else + [view lockFocus]; +#endif + focus_view = view; +/*if (view) debug_lock++; */ + } +#ifdef NS_IMPL_GNUSTEP + else + { + /* more than one rect being drawn into */ + if (view && r) + { + [view unlockFocus]; /* add prev rect to redraw list */ + [view lockFocusInRect: u]; /* focus for draw in new rect */ + } + } +#endif + } +#ifdef NS_IMPL_GNUSTEP + else + { + /* in batch mode, but in GNUstep must still track rectangles explicitly */ + uRect = (r ? NSUnionRect (uRect, u) : [FRAME_NS_VIEW (f) visibleRect]); + } +#endif + + /*23: clipping */ + if (r) + { + [[NSGraphicsContext currentContext] saveGraphicsState]; + if (n == 2) + NSRectClipList (r, 2); + else + NSRectClip (*r); + gsaved = YES; + } +} + + +static void +ns_unfocus (struct frame *f) +/* -------------------------------------------------------------------------- + Internal: Remove focus on given frame + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_unfocus); + + if (gsaved) + { + [[NSGraphicsContext currentContext] restoreGraphicsState]; + gsaved = NO; + } + + if (f != ns_updating_frame) + { + if (focus_view != NULL) + { + [focus_view unlockFocus]; + [[focus_view window] flushWindow]; + focus_view = NULL; +/*debug_lock--; */ + } + } +} + + +static void +ns_clip_to_row (struct window *w, struct glyph_row *row, int area, GC gc) +/* -------------------------------------------------------------------------- + 23: Internal (but parallels other terms): Focus drawing on given row + -------------------------------------------------------------------------- */ +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + NSRect clip_rect; + int window_x, window_y, window_width; + + window_box (w, area, &window_x, &window_y, &window_width, 0); + + clip_rect.origin.x = window_x - FRAME_INTERNAL_BORDER_WIDTH (f); + clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y)); + clip_rect.origin.y = max (clip_rect.origin.y, window_y); + clip_rect.size.width = window_width + 2 * FRAME_INTERNAL_BORDER_WIDTH (f); + clip_rect.size.height = row->visible_height; + + /* allow a full-height row at the top when requested + (used to draw fringe all the way through internal border area) */ + if (gc && clip_rect.origin.y < 5) + { + clip_rect.origin.y -= FRAME_INTERNAL_BORDER_WIDTH (f); + clip_rect.size.height += FRAME_INTERNAL_BORDER_WIDTH (f); + } + + /* likewise at bottom */ + if (gc && + FRAME_PIXEL_HEIGHT (f) - (clip_rect.origin.y + clip_rect.size.height) < 5) + clip_rect.size.height += FRAME_INTERNAL_BORDER_WIDTH (f); + + ns_focus (f, &clip_rect, 1); +} + + +static void +ns_ring_bell () +/* -------------------------------------------------------------------------- + "Beep" routine + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_ring_bell); + if (visible_bell) + { + NSAutoreleasePool *pool; + struct frame *frame = SELECTED_FRAME (); + NSView *view; + + BLOCK_INPUT; + pool = [[NSAutoreleasePool alloc] init]; + + view = FRAME_NS_VIEW (frame); + if (view != nil) + { + NSRect r, surr; + NSPoint dim = NSMakePoint (128, 128); + + r = [view bounds]; + r.origin.x += (r.size.width - dim.x) / 2; + r.origin.y += (r.size.height - dim.y) / 2; + r.size.width = dim.x; + r.size.height = dim.y; + /* PENDING: cacheImageInRect under GNUSTEP does not account for + offset in x_set_window_size, so overestimate (4 fine on Cocoa) */ + surr = NSInsetRect (r, -10, -10); + ns_focus (frame, &surr, 1); + [[view window] cacheImageInRect: surr]; + [ns_lookup_indexed_color (NS_FACE_FOREGROUND + (FRAME_DEFAULT_FACE (frame)), frame) set]; + NSRectFill (r); + [[view window] flushWindow]; + ns_timeout (150000); + [[view window] restoreCachedImage]; + [[view window] flushWindow]; + ns_unfocus (frame); + } + [pool release]; + UNBLOCK_INPUT; + } + else + { + NSBeep (); + } +} + + +static void +ns_reset_terminal_modes (struct terminal *terminal) +/* Externally called as hook */ +{ + NSTRACE (ns_reset_terminal_modes); +} + +static void +ns_set_terminal_modes (struct terminal *terminal) +/* Externally called as hook */ +{ + NSTRACE (ns_set_terminal_modes); +} + + + +/* ========================================================================== + + Frame / window manager related functions + + ========================================================================== */ + + +static void +ns_raise_frame (struct frame *f) +/* -------------------------------------------------------------------------- + Bring window to foreground and make it active + -------------------------------------------------------------------------- */ +{ + NSView *view = FRAME_NS_VIEW (f); + check_ns (); + BLOCK_INPUT; + [[view window] makeKeyAndOrderFront: NSApp]; + UNBLOCK_INPUT; +} + + +static void +ns_lower_frame (struct frame *f) +/* -------------------------------------------------------------------------- + Send window to back + -------------------------------------------------------------------------- */ +{ + NSView *view = FRAME_NS_VIEW (f); + check_ns (); + BLOCK_INPUT; + [[view window] orderBack: NSApp]; + UNBLOCK_INPUT; +} + + +static void +ns_frame_raise_lower (struct frame *f, int raise) +/* -------------------------------------------------------------------------- + External (hook) + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_frame_raise_lower); + + if (raise) + ns_raise_frame (f); + else + ns_lower_frame (f); +} + + +static void +ns_frame_rehighlight (struct frame *frame) +/* -------------------------------------------------------------------------- + External (hook): called on things like window switching within frame + -------------------------------------------------------------------------- */ +{ + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (frame); + struct frame *old_highlight = dpyinfo->ns_highlight_frame; + + NSTRACE (ns_frame_rehighlight); + if (dpyinfo->ns_focus_frame) + { + dpyinfo->ns_highlight_frame = + (FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->ns_focus_frame)) + ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->ns_focus_frame)) + : dpyinfo->ns_focus_frame); + if (!FRAME_LIVE_P (dpyinfo->ns_highlight_frame)) + { + FRAME_FOCUS_FRAME (dpyinfo->ns_focus_frame) = Qnil; + dpyinfo->ns_highlight_frame = dpyinfo->ns_focus_frame; + } + } + else + dpyinfo->ns_highlight_frame = 0; + + if (dpyinfo->ns_highlight_frame && + dpyinfo->ns_highlight_frame != old_highlight) + { + /* as of 20080602 the lower and raise are superfluous */ + if (old_highlight) + { + /*ns_lower_frame (old_highlight); */ + x_update_cursor (old_highlight, 1); + } + if (dpyinfo->ns_highlight_frame) + { + /*ns_raise_frame (dpyinfo->ns_highlight_frame); */ + x_update_cursor (dpyinfo->ns_highlight_frame, 1); + } + } +} + + +void +x_make_frame_visible (struct frame *f) +/* -------------------------------------------------------------------------- + External: Show the window (X11 semantics) + -------------------------------------------------------------------------- */ +{ + NSTRACE (x_make_frame_visible); + /* PENDING: at some points in past this was not needed, as the only place that + called this (frame.c:Fraise_frame ()) also called raise_lower; + if this ends up the case again, comment this out again. */ + if (!FRAME_VISIBLE_P (f)) + ns_raise_frame (f); +} + + +void +x_make_frame_invisible (struct frame *f) +/* -------------------------------------------------------------------------- + External: Hide the window (X11 semantics) + -------------------------------------------------------------------------- */ +{ + NSView * view = FRAME_NS_VIEW (f); + NSTRACE (x_make_frame_invisible); + check_ns (); + [[view window] orderOut: NSApp]; +} + + +void +x_iconify_frame (struct frame *f) +/* -------------------------------------------------------------------------- + External: Iconify window + -------------------------------------------------------------------------- */ +{ + NSView * view = FRAME_NS_VIEW (f); + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (f); + NSTRACE (x_iconify_frame); + check_ns (); + + if (dpyinfo->ns_highlight_frame == f) + dpyinfo->ns_highlight_frame = 0; + + if ([[view window] windowNumber] <= 0) + { + /* the window is still deferred. Make it very small, bring it + on screen and order it out. */ + NSRect s = { { 100, 100}, {0, 0} }; + NSRect t; + t = [[view window] frame]; + [[view window] setFrame: s display: NO]; + [[view window] orderBack: NSApp]; + [[view window] orderOut: NSApp]; + [[view window] setFrame: t display: NO]; + } + [[view window] miniaturize: NSApp]; +} + + +void +x_destroy_window (struct frame *f) +/* -------------------------------------------------------------------------- + External: Delete the window + -------------------------------------------------------------------------- */ +{ + NSView *view = FRAME_NS_VIEW (f); + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (f); + NSTRACE (x_destroy_window); + check_ns (); + + [(EmacsView *)view setWindowClosing: YES]; /* may not have been informed */ + + BLOCK_INPUT; + + free_frame_menubar (f); + + if (FRAME_FACE_CACHE (f)) + free_frame_faces (f); + + if (f == dpyinfo->ns_focus_frame) + dpyinfo->ns_focus_frame = 0; + if (f == dpyinfo->ns_highlight_frame) + dpyinfo->ns_highlight_frame = 0; + if (f == dpyinfo->mouse_face_mouse_frame) + { + dpyinfo->mouse_face_beg_row = dpyinfo->mouse_face_beg_col = -1; + dpyinfo->mouse_face_end_row = dpyinfo->mouse_face_end_col = -1; + dpyinfo->mouse_face_window = Qnil; + dpyinfo->mouse_face_deferred_gc = 0; + dpyinfo->mouse_face_mouse_frame = 0; + } + + xfree (f->output_data.ns); + + [[view window] close]; + [view release]; + + ns_window_num--; + UNBLOCK_INPUT; +} + + +void +x_set_offset (struct frame *f, int xoff, int yoff, int change_grav) +/* -------------------------------------------------------------------------- + External: Position the window + -------------------------------------------------------------------------- */ +{ + NSScreen *screen; + NSView *view = FRAME_NS_VIEW (f); + + NSTRACE (x_set_offset); + + BLOCK_INPUT; + + f->left_pos = xoff; + f->top_pos = yoff; +#ifdef NS_IMPL_GNUSTEP + if (xoff < 100) + f->left_pos = 100; /* don't overlap menu */ +#endif + if (view != nil && (screen = [[view window] screen])) + [[view window] setFrameTopLeftPoint: + NSMakePoint (SCREENMAXBOUND (f->left_pos), + SCREENMAXBOUND ([screen frame].size.height + - NS_TOP_POS (f)))]; + UNBLOCK_INPUT; +} + + +void +x_set_window_size (struct frame *f, int change_grav, int cols, int rows) +/* -------------------------------------------------------------------------- + Adjust window pixel size based on given character grid size + Impl is a bit more complex than other terms, need to do some + internal clipping and also pay attention to screen constraints. + -------------------------------------------------------------------------- */ +{ + EmacsView *view = FRAME_NS_VIEW (f); + EmacsToolbar *toolbar = [view toolbar]; + NSWindow *window = [view window]; + NSScreen *screen = [window screen]; + NSRect wr = [window frame]; + int tb = FRAME_EXTERNAL_TOOL_BAR (f); + int pixelwidth, pixelheight; + static int oldRows, oldCols, oldFontWidth, oldFontHeight; + static int oldTB; + static struct frame *oldF; + + NSTRACE (x_set_window_size); + + if (view == nil || + (f == oldF + && rows == oldRows && cols == oldCols + && oldFontWidth == FRAME_COLUMN_WIDTH (f) + && oldFontHeight == FRAME_LINE_HEIGHT (f) + && oldTB == tb)) + return; + +/*fprintf (stderr, "\tsetWindowSize: %d x %d, font size %d x %d\n", cols, rows, FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f)); */ + + BLOCK_INPUT; + + check_frame_size (f, &rows, &cols); + oldF = f; + oldRows = rows; + oldCols = cols; + oldFontWidth = FRAME_COLUMN_WIDTH (f); + oldFontHeight = FRAME_LINE_HEIGHT (f); + oldTB = tb; + + f->scroll_bar_actual_width = NS_SCROLL_BAR_WIDTH (f); + compute_fringe_widths (f, 0); + + pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols); + pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows); + + /* If we have a change in toolbar display, calculate height */ + if (tb) + /* PENDING: GNUstep has not yet implemented the first method below, added + in Panther, however the second is incorrect under Cocoa. */ +#ifdef NS_IMPL_GNUSTEP + FRAME_NS_TOOLBAR_HEIGHT (f) = + NSHeight ([NSWindow frameRectForContentRect: NSMakeRect (0, 0, 0, 0) + styleMask: [window styleMask]]) + - FRAME_NS_TITLEBAR_HEIGHT (f); +#else + FRAME_NS_TOOLBAR_HEIGHT (f) = 32; + /* actually get wrong result here if toolbar not yet displayed + NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)]) + - FRAME_NS_TITLEBAR_HEIGHT (f); */ +#endif + else + FRAME_NS_TOOLBAR_HEIGHT (f) = 0; + + wr.size.width = pixelwidth + f->border_width; + wr.size.height = pixelheight + FRAME_NS_TITLEBAR_HEIGHT (f) + + FRAME_NS_TOOLBAR_HEIGHT (f); + + /* constrain to screen if we can */ + if (screen) + { + NSSize sz = [screen visibleFrame].size; + NSSize ez = { wr.size.width - sz.width, wr.size.height - sz.height }; + if (ez.width > 0) + { + int cr = ez.width / FRAME_COLUMN_WIDTH (f) + 1; + cols -= cr; + oldCols = cols; + wr.size.width -= cr * FRAME_COLUMN_WIDTH (f); + pixelwidth -= cr * FRAME_COLUMN_WIDTH (f); + } + if (ez.height > 0) + { + int rr = ez.height / FRAME_LINE_HEIGHT (f) + 1; + rows -= rr; + oldRows = rows; + wr.size.height -= rr * FRAME_LINE_HEIGHT (f); + pixelheight -= rr * FRAME_LINE_HEIGHT (f); + } + wr.origin.x = f->left_pos; + wr.origin.y = [screen frame].size.height - NS_TOP_POS (f) + - wr.size.height; + } + + [view setRows: rows andColumns: cols]; + [window setFrame: wr display: YES]; + +/*fprintf (stderr, "\tx_set_window_size %d, %d\t%d, %d\n", cols, rows, pixelwidth, pixelheight); */ + + /* This is a trick to compensate for Emacs' managing the scrollbar area + as a fixed number of standard character columns. Instead of leaving + blank space for the extra, we chopped it off above. Now for + left-hand scrollbars, we shift all rendering to the left by the + difference between the real width and Emacs' imagined one. For + right-hand bars, don't worry about it since the extra is never used. + (Obviously doesn't work for vertically split windows tho..) */ + NSPoint origin = FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f) + ? NSMakePoint (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f) + - NS_SCROLL_BAR_WIDTH (f), 0) + : NSMakePoint (0, 0); + [view setFrame: NSMakeRect (0, 0, pixelwidth, pixelheight)]; + [view setBoundsOrigin: origin]; + + change_frame_size (f, rows, cols, 0, 1, 0); /* pretend, delay, safe */ + FRAME_PIXEL_WIDTH (f) = pixelwidth; + FRAME_PIXEL_HEIGHT (f) = pixelheight; +/* SET_FRAME_GARBAGED (f); // this short-circuits expose call in drawRect */ + + mark_window_cursors_off (XWINDOW (f->root_window)); + cancel_mouse_face (f); + + UNBLOCK_INPUT; +} + + +/* ========================================================================== + + Color management + + ========================================================================== */ + +NSColor * +ns_lookup_indexed_color (unsigned long idx, struct frame *f) +{ + struct ns_color_table *color_table = FRAME_NS_DISPLAY_INFO (f)->color_table; + return color_table->colors[idx]; +} + + +unsigned long +ns_index_color (NSColor *color, struct frame *f) +{ + struct ns_color_table *color_table = FRAME_NS_DISPLAY_INFO (f)->color_table; + int idx; + NSNumber *index; + + if (!color_table->colors) + { + color_table->size = NS_COLOR_CAPACITY; + color_table->avail = 1; /* skip idx=0 as marker */ + color_table->colors = + (NSColor **)xmalloc (color_table->size * sizeof (NSColor *)); + color_table->empty_indices = [[NSMutableSet alloc] init]; + } + + /* do we already have this color ? */ + { + int i; + for (i = 1; i < color_table->avail; i++) + { + if (color_table->colors[i] && [color_table->colors[i] isEqual: color]) + { + [color_table->colors[i] retain]; + return i; + } + } + } + + if ([color_table->empty_indices count] > 0) + { + index = [color_table->empty_indices anyObject]; + [color_table->empty_indices removeObject: index]; + idx = [index unsignedIntValue]; + } + else + { + if (color_table->avail == color_table->size) + { + color_table->size += NS_COLOR_CAPACITY; + color_table->colors = + (NSColor **)xrealloc (color_table->colors, + color_table->size * sizeof (NSColor *)); + } + idx = color_table->avail++; + index = [NSNumber numberWithUnsignedInt: idx]; + } + + color_table->colors[idx] = color; + [color retain]; +/*fprintf(stderr, "color_table: allocated %d\n",idx);*/ + return idx; +} + + +void +ns_free_indexed_color (unsigned long idx, struct frame *f) +{ + struct ns_color_table *color_table = FRAME_NS_DISPLAY_INFO (f)->color_table; + NSColor *color; + if (!idx) + return; + color = color_table->colors[idx]; + [color release]; + color_table->colors[idx] = nil; + [color_table->empty_indices addObject: [NSNumber numberWithUnsignedInt: idx]]; +/*fprintf(stderr, "color_table: FREED %d\n",idx);*/ +} + + +static int +ns_get_color (const char *name, NSColor **col) +/* -------------------------------------------------------------------------- + Parse a color name +/* -------------------------------------------------------------------------- +/* On *Step, we recognize several color formats, in addition to a catalog + of colors found in the file Emacs.clr. Color formats include: + - #rrggbb or RGBrrggbb where rr, gg, bb specify red, green and blue in hex + - ARGBaarrggbb is similar, with aa being the alpha channel (FF = opaque) + - HSVhhssvv and AHSVaahhssvv are similar for hue, saturation, value + - CMYKccmmyykk is similar for cyan, magenta, yellow, black. */ +{ + NSColor * new = nil; + const char *hex = NULL; + enum { rgb, argb, hsv, ahsv, cmyk, gray } color_space; + NSString *nsname = [NSString stringWithUTF8String: name]; + +/*fprintf (stderr, "ns_get_color: '%s'\n", name); */ + BLOCK_INPUT; + + if ([nsname isEqualToString: @"ns_selection_color"]) + { + nsname = ns_selection_color; + name = [ns_selection_color UTF8String]; + } + + if (name[0] == '0' || name[0] == '1' || name[0] == '.') + { + /* RGB decimal */ + NSScanner *scanner = [NSScanner scannerWithString: nsname]; + float r, g, b; + [scanner scanFloat: &r]; + [scanner scanFloat: &g]; + [scanner scanFloat: &b]; + *col = [NSColor colorWithCalibratedRed: r green: g blue: b alpha: 1.0]; + UNBLOCK_INPUT; + return 0; + } + + /* 23: PENDING: emacs seems to downcase everything before passing it here, + which we can work around, except for GRAY, since gray##, where ## is + decimal between 0 and 99, is also an X11 colorname. */ + if (name[0] == '#') /* X11 format */ + { + hex = name + 1; + color_space = rgb; + } + else if (!memcmp (name, "RGB", 3) || !memcmp (name, "rgb", 3)) + { + hex = name + 3; + color_space = rgb; + } + else if (!memcmp (name, "ARGB", 4) || !memcmp (name, "argb", 4)) + { + hex = name + 4; + color_space = argb; + } + else if (!memcmp (name, "HSV", 3) || !memcmp (name, "hsv", 3)) + { + hex = name + 3; + color_space = hsv; + } + else if (!memcmp (name, "AHSV", 4) || !memcmp (name, "ahsv", 4)) + { + hex = name + 4; + color_space = ahsv; + } + else if (!memcmp (name, "CMYK", 4) || !memcmp (name, "cmyk", 4)) + { + hex = name + 4; + color_space = cmyk; + } + else if (!memcmp (name, "GRAY", 4) /*|| !memcmp (name, "gray", 4)*/) + { + hex = name + 4; + color_space = gray; + } + + /* Direct colors (hex values) */ + if (hex) + { + unsigned int color = 0; + if (sscanf (hex, "%x", &color)) + { + float f1 = ((color >> 24) & 0xff) / 255.0; + float f2 = ((color >> 16) & 0xff) / 255.0; + float f3 = ((color >> 8) & 0xff) / 255.0; + float f4 = ((color ) & 0xff) / 255.0; + + switch (color_space) + { + case rgb: + *col = [NSColor colorWithCalibratedRed: f2 + green: f3 + blue: f4 + alpha: 1.0]; + break; + case argb: + *col = [NSColor colorWithCalibratedRed: f2 + green: f3 + blue: f4 + alpha: f1]; + break; + case hsv: + *col = [NSColor colorWithCalibratedHue: f2 + saturation: f3 + brightness: f4 + alpha: 1.0]; + break; + case ahsv: + *col = [NSColor colorWithCalibratedHue: f2 + saturation: f3 + brightness: f4 + alpha: f1]; + break; + case gray: + *col = [NSColor colorWithCalibratedWhite: f3 alpha: f4]; + break; + case cmyk: + *col = [NSColor colorWithDeviceCyan: f1 + magenta: f2 + yellow: f3 + black: f4 + alpha: 1.0]; + break; + } + *col = [*col colorUsingColorSpaceName: NSCalibratedRGBColorSpace]; + UNBLOCK_INPUT; + return 0; + } + } + + /* Otherwise, color is expected to be from a list */ + { + NSEnumerator *lenum, *cenum; + NSString *name; + NSColorList *clist; +#ifdef NS_IMPL_GNUSTEP + /* PENDING: who is wrong, the requestor or the implementation? */ + if ([nsname compare: @"Highlight" options: NSCaseInsensitiveSearch] + == NSOrderedSame) + nsname = @"highlightColor"; +#endif + if ([nsname compare: @"dark blue" options: NSCaseInsensitiveSearch] + == NSOrderedSame + || [nsname compare: @"darkblue" options: NSCaseInsensitiveSearch] + == NSOrderedSame) + nsname = @"navy blue"; + + lenum = [[NSColorList availableColorLists] objectEnumerator]; + while ( (clist = [lenum nextObject]) && new == nil) + { + cenum = [[clist allKeys] objectEnumerator]; + while ( (name = [cenum nextObject]) && new == nil ) + { + if ([name compare: nsname + options: NSCaseInsensitiveSearch] == NSOrderedSame ) + new = [clist colorWithKey: name]; + } + } + } + + if ( new ) + *col = [new colorUsingColorSpaceName: NSCalibratedRGBColorSpace]; +/* else + NSLog (@"Failed to find color '%@'", nsname); */ + UNBLOCK_INPUT; + return new ? 0 : 1; +} + + +static NSColor * +ns_get_color_default (const char *name, NSColor *dflt) +/* -------------------------------------------------------------------------- + Parse a color or use a default value + -------------------------------------------------------------------------- */ +{ + NSColor * col; + + if (ns_get_color (name, &col)) + return dflt; + else + return col; +} + + +int +ns_lisp_to_color (Lisp_Object color, NSColor **col) +/* -------------------------------------------------------------------------- + Convert a Lisp string object to a NS color + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_lisp_to_color); + if (XTYPE (color) == Lisp_String) + return ns_get_color (XSTRING (color)->data, col); + else if (XTYPE (color) == Lisp_Symbol) + return ns_get_color (XSTRING (XSYMBOL (color)->xname)->data, col); + return 1; +} + + +Lisp_Object +ns_color_to_lisp (NSColor *col) +/* -------------------------------------------------------------------------- + Convert a color to a lisp string with the RGB equivalent + -------------------------------------------------------------------------- */ +{ + float red, green, blue, alpha, gray; + char buf[1024]; + const char *str; + NSTRACE (ns_color_to_lisp); + + BLOCK_INPUT; + if ([[col colorSpaceName] isEqualToString: NSNamedColorSpace]) + + if ((str =[[col colorNameComponent] UTF8String])) + { + UNBLOCK_INPUT; + return build_string ((char *)str); + } + + [[col colorUsingColorSpaceName: NSCalibratedRGBColorSpace] + getRed: &red green: &green blue: &blue alpha: &alpha]; + if (red ==green && red ==blue) + { + [[col colorUsingColorSpaceName: NSCalibratedWhiteColorSpace] + getWhite: &gray alpha: &alpha]; + snprintf (buf, sizeof (buf), "GRAY%02.2lx%02.2lx", + lrint (gray * 0xff), lrint (alpha * 0xff)); + UNBLOCK_INPUT; + return build_string (buf); + } + + snprintf (buf, sizeof (buf), "ARGB%02.2lx%02.2lx%02.2lx%02.2lx", + lrint (alpha*0xff), + lrint (red*0xff), lrint (green*0xff), lrint (blue*0xff)); + + UNBLOCK_INPUT; + return build_string (buf); +} + + +int +ns_defined_color (struct frame *f, char *name, XColor *color_def, int alloc, + char makeIndex) +/* -------------------------------------------------------------------------- + 23: Return 1 if named color found, and set color_def rgb accordingly. + If makeIndex and alloc are nonzero put the color in the color_table, + and set color_def pixel to the resulting index. + If makeIndex is zero, set color_def pixel to ARGB. + Return 0 if not found + -------------------------------------------------------------------------- */ +{ + NSColor *temp; + float r, g, b, a; + int notFound = ns_get_color (name, &temp); + + NSTRACE (ns_defined_color); + + if (notFound) + return 0; + + if (makeIndex && alloc) + color_def->pixel = ns_index_color(temp, f);//[temp retain]; + + [temp getRed: &r green: &g blue: &b alpha: &a]; + color_def->red = r * 256; + color_def->green = g * 256; + color_def->blue = b * 256; + + if (!makeIndex) + color_def->pixel = + ARGB_TO_ULONG((int)(a*256), + color_def->red, color_def->green, color_def->blue); + + return 1; +} + + +unsigned long +ns_get_rgb_color (struct frame *f, float r, float g, float b, float a) +/* -------------------------------------------------------------------------- + return an autoreleased RGB color + -------------------------------------------------------------------------- */ +{ +/*static int c = 1; fprintf (stderr, "color request %d\n", c++); */ + if (r < 0.0) r = 0.0; + else if (r > 1.0) r = 1.0; + if (g < 0.0) g = 0.0; + else if (g > 1.0) g = 1.0; + if (b < 0.0) b = 0.0; + else if (b > 1.0) b = 1.0; + if (a < 0.0) a = 0.0; + else if (a > 1.0) a = 1.0; + return (unsigned long) ns_index_color( + [NSColor colorWithCalibratedRed: r green: g blue: b alpha: a], f); +} + + + +/* ========================================================================== + + Mouse handling + + ========================================================================== */ + + +void +x_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y) +/* -------------------------------------------------------------------------- + Programmatically reposition mouse pointer in pixel coordinates + -------------------------------------------------------------------------- */ +{ + NSTRACE (x_set_mouse_pixel_position); + ns_raise_frame (f); +#if 0 + /*PENDING: this does not work, and what about GNUstep? */ +#ifdef NS_IMPL_COCOA + [FRAME_NS_VIEW (f) lockFocus]; + PSsetmouse ((float)pix_x, (float)pix_y); + [FRAME_NS_VIEW (f) unlockFocus]; +#endif +#endif +} + + +void +x_set_mouse_position (struct frame *f, int h, int v) +/* -------------------------------------------------------------------------- + Programmatically reposition mouse pointer in character coordinates + -------------------------------------------------------------------------- */ +{ + int pix_x, pix_y; + + pix_x = FRAME_COL_TO_PIXEL_X (f, h) + FRAME_COLUMN_WIDTH (f) / 2; + pix_y = FRAME_LINE_TO_PIXEL_Y (f, v) + FRAME_LINE_HEIGHT (f) / 2; + + if (pix_x < 0) pix_x = 0; + if (pix_x > FRAME_PIXEL_WIDTH (f)) pix_x = FRAME_PIXEL_WIDTH (f); + + if (pix_y < 0) pix_y = 0; + if (pix_y > FRAME_PIXEL_HEIGHT (f)) pix_y = FRAME_PIXEL_HEIGHT (f); + + x_set_mouse_pixel_position (f, pix_x, pix_y); +} + + +static int +note_mouse_movement (struct frame *frame, float x, float y) +/* ------------------------------------------------------------------------ + Called by EmacsView on mouseMovement events. Passes on + to emacs mainstream code if we moved off of a rect of interest + known as last_mouse_glyph. + ------------------------------------------------------------------------ */ +{ + NSTRACE (note_mouse_movement); + + XSETFRAME (last_mouse_motion_frame, frame); + + /* Note, this doesn't get called for enter/leave, since we don't have a + position. Those are taken care of in the corresponding NSView methods. */ + + /* has movement gone beyond last rect we were tracking? */ + if (x < last_mouse_glyph.origin.x || + x >= (last_mouse_glyph.origin.x + last_mouse_glyph.size.width) || + y < last_mouse_glyph.origin.y || + y >= (last_mouse_glyph.origin.y + last_mouse_glyph.size.height)) + { + frame->mouse_moved = 1; + note_mouse_highlight (frame, x, y); + remember_mouse_glyph (frame, x, y, &last_mouse_glyph); + return 1; + } + + return 0; +} + + +static void +ns_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, + enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y, + unsigned long *time) +/* -------------------------------------------------------------------------- + External (hook): inform emacs about mouse position and hit parts. + If a scrollbar is being dragged, set bar_window, part, x, y, time. + x & y should be position in the scrollbar (the whole bar, not the handle) + and length of scrollbar respectively + -------------------------------------------------------------------------- */ +{ + id view; + NSPoint position; + int xchar, ychar; + Lisp_Object frame, tail; + struct frame *f; + struct ns_display_info *dpyinfo; + + NSTRACE (ns_mouse_position); + + if (*fp == NULL) + { + fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n"); + return; + } + + dpyinfo = FRAME_NS_DISPLAY_INFO (*fp); + + BLOCK_INPUT; + + if (last_mouse_scroll_bar != nil && insist == 0) + { + /* PENDING: we do not use this path at the moment because drag events will + go directly to the EmacsScroller. Leaving code in for now. */ + [last_mouse_scroll_bar getMouseMotionPart: (int *)part window: bar_window + x: x y: y]; + if (time) *time = last_mouse_movement_time; + last_mouse_scroll_bar = nil; + } + else + { + /* Clear the mouse-moved flag for every frame on this display. */ + FOR_EACH_FRAME (tail, frame) + if (FRAME_NS_P (XFRAME (frame)) + && FRAME_NS_DISPLAY (XFRAME (frame)) == FRAME_NS_DISPLAY (*fp)) + XFRAME (frame)->mouse_moved = 0; + + last_mouse_scroll_bar = nil; + if (last_mouse_frame && FRAME_LIVE_P (last_mouse_frame)) + f = last_mouse_frame; + else + f = dpyinfo->ns_focus_frame ? dpyinfo->ns_focus_frame + : SELECTED_FRAME (); + + if (f && f->output_data.ns) /*PENDING: 2nd check no longer needed? */ + { + view = FRAME_NS_VIEW (*fp); + + position = [[view window] mouseLocationOutsideOfEventStream]; + position = [view convertPoint: position fromView: nil]; + remember_mouse_glyph (f, position.x, position.y, &last_mouse_glyph); +/*fprintf (stderr, "ns_mouse_position: %.0f, %.0f\n", position.x, position.y); */ + + if (bar_window) *bar_window = Qnil; + if (part) *part = 0; /*scroll_bar_handle; */ + + if (x) XSETINT (*x, lrint (position.x)); + if (y) XSETINT (*y, lrint (position.y)); + if (time) *time = last_mouse_movement_time; + *fp = f; + } + } + + UNBLOCK_INPUT; +} + + +static void +ns_frame_up_to_date (struct frame *f) +/* -------------------------------------------------------------------------- + External (hook): Fix up mouse highlighting right after a full update. + Some highlighting was deferred if GC was happening during + note_mouse_highlight (), while other highlighting was deferred for update. + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_frame_up_to_date); + + if (FRAME_NS_P (f)) + { + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (f); + if ((dpyinfo->mouse_face_deferred_gc||f ==dpyinfo->mouse_face_mouse_frame) + /*&& dpyinfo->mouse_face_mouse_frame*/) + { + BLOCK_INPUT; + if (dpyinfo->mouse_face_mouse_frame) + note_mouse_highlight (dpyinfo->mouse_face_mouse_frame, + dpyinfo->mouse_face_mouse_x, + dpyinfo->mouse_face_mouse_y); + dpyinfo->mouse_face_deferred_gc = 0; + UNBLOCK_INPUT; + } + } +} + + +void +ns_define_frame_cursor (struct frame *f, Cursor cursor) +/* -------------------------------------------------------------------------- + External (RIF): set frame mouse pointer type. + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_define_frame_cursor); + if (FRAME_POINTER_TYPE (f) != cursor) + { + EmacsView *view = FRAME_NS_VIEW (f); + FRAME_POINTER_TYPE (f) = cursor; + [[view window] invalidateCursorRectsForView: view]; + } +} + + + +/* ========================================================================== + + Keyboard handling + + ========================================================================== */ + + +static unsigned +ns_convert_key (unsigned code) +/* -------------------------------------------------------------------------- + Internal call used by NSView-keyDown. + -------------------------------------------------------------------------- */ +{ + const unsigned last_keysym = (sizeof (convert_ns_to_X_keysym) + / sizeof (convert_ns_to_X_keysym[0])); + unsigned keysym; + /* An array would be faster, but less easy to read. */ + for (keysym = 0; keysym < last_keysym; keysym += 2) + if (code == convert_ns_to_X_keysym[keysym]) + return 0xFF00 | convert_ns_to_X_keysym[keysym+1]; + return 0; +/* if decide to use keyCode and Carbon table, use this line: + return code > 0xff ? 0 : 0xFF00 | ns_keycode_to_xkeysym_table[code]; */ +} + + +char * +x_get_keysym_name (int keysym) +/* -------------------------------------------------------------------------- + Called by keyboard.c. Not sure if the return val is important, except + that it be unique. + -------------------------------------------------------------------------- */ +{ + static char value[16]; + NSTRACE (x_get_keysym_name); + sprintf (value, "%d", keysym); + return value; +} + + + +/* ========================================================================== + + Block drawing operations + + ========================================================================== */ + + +static void +ns_redraw_scroll_bars (struct frame *f) +{ + int i; + id view; + NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews]; + NSTRACE (ns_judge_scroll_bars); + for (i =[subviews count]-1; i >= 0; i--) + { + view = [subviews objectAtIndex: i]; + if (![view isKindOfClass: [EmacsScroller class]]) continue; + [view display]; + } +} + + +void +ns_clear_frame (struct frame *f) +/* -------------------------------------------------------------------------- + External (hook): Erase the entire frame + -------------------------------------------------------------------------- */ +{ + NSView *view = FRAME_NS_VIEW (f); + NSRect r; + + NSTRACE (ns_clear_frame); + if (ns_in_resize) + return; + + /* comes on initial frame because we have + after-make-frame-functions = select-frame */ + if (!FRAME_DEFAULT_FACE (f)) + return; + + mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f))); + + output_cursor.hpos = output_cursor.vpos = 0; + output_cursor.x = -1; + + r = [view bounds]; + + BLOCK_INPUT; + ns_focus (f, &r, 1); + [ns_lookup_indexed_color (NS_FACE_BACKGROUND (FRAME_DEFAULT_FACE (f)), f) set]; + NSRectFill (r); + ns_unfocus (f); + +#ifdef NS_IMPL_COCOA + [[view window] display]; /* redraw resize handle */ +#endif + + /* as of 2006/11 or so this is now needed */ + ns_redraw_scroll_bars (f); + UNBLOCK_INPUT; +} + + +void +ns_clear_frame_area (struct frame *f, int x, int y, int width, int height) +/* -------------------------------------------------------------------------- + 23: External (RIF): Clear section of frame + -------------------------------------------------------------------------- */ +{ + NSRect r = NSMakeRect (x, y, width, height); + NSView *view = FRAME_NS_VIEW (f); + struct face *face = FRAME_DEFAULT_FACE (f); + + if (!view || !face) + return; + + r = NSIntersectionRect (r, [view frame]); + ns_focus (f, &r, 1); + [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set]; + +#ifdef NS_IMPL_COCOA + { + /* clip out the resize handle */ + NSWindow *window = [FRAME_NS_VIEW (f) window]; + NSRect ir = + [view convertRect: ns_resize_handle_rect (window) fromView: nil]; + + ir = NSIntersectionRect (r, ir); + if (NSIsEmptyRect (ir)) + { +#endif + + NSRectFill (r); + +#ifdef NS_IMPL_COCOA + } + else + { + NSRect r1 = r, r2 = r; /* upper and lower non-intersecting */ + r1.size.height -= ir.size.height; + r2.origin.y += r1.size.height; + r2.size.width -= ir.size.width; + r2.size.height = ir.size.height; + NSRectFill (r1); + NSRectFill (r2); + } + } +#endif + + ns_unfocus (f); + return; +} + + +static void +ns_scroll_run (struct window *w, struct run *run) +/* -------------------------------------------------------------------------- + 23: External (RIF): Insert or delete n lines at line vpos + -------------------------------------------------------------------------- */ +{ + struct frame *f = XFRAME (w->frame); + int x, y, width, height, from_y, to_y, bottom_y; + + NSTRACE (ns_scroll_run); + + /* begin copy from other terms */ + /* Get frame-relative bounding box of the text display area of W, + without mode lines. Include in this box the left and right + fringe of W. */ + window_box (w, -1, &x, &y, &width, &height); + + from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y); + to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y); + bottom_y = y + height; + + if (to_y < from_y) + { + /* Scrolling up. Make sure we don't copy part of the mode + line at the bottom. */ + if (from_y + run->height > bottom_y) + height = bottom_y - from_y; + else + height = run->height; + } + else + { + /* Scolling down. Make sure we don't copy over the mode line. + at the bottom. */ + if (to_y + run->height > bottom_y) + height = bottom_y - to_y; + else + height = run->height; + } + /* end copy from other terms */ + + if (height == 0) + return; + + BLOCK_INPUT; + + updated_window = w; + x_clear_cursor (w); + + { + NSRect srcRect = NSMakeRect (x, from_y, width, height); + NSRect dstRect = NSMakeRect (x, to_y, width, height); + NSPoint dstOrigin = NSMakePoint (x, to_y); + + ns_focus (f, &dstRect, 1); + NSCopyBits (0, srcRect , dstOrigin); + ns_unfocus (f); + } + + UNBLOCK_INPUT; +} + + +static void +ns_after_update_window_line (struct glyph_row *desired_row) +/* -------------------------------------------------------------------------- + 23: External (RIF): preparatory to fringe update after text was updated + -------------------------------------------------------------------------- */ +{ + struct window *w = updated_window; + struct frame *f; + int width, height; + + NSTRACE (ns_after_update_window_line); + + /* begin copy from other terms */ + xassert (w); + + if (!desired_row->mode_line_p && !w->pseudo_window_p) + desired_row->redraw_fringe_bitmaps_p = 1; + + /* When a window has disappeared, make sure that no rest of + full-width rows stays visible in the internal border. + Under NS this is drawn inside the fringes. */ + if (windows_or_buffers_changed + && (f = XFRAME (w->frame), + width = FRAME_INTERNAL_BORDER_WIDTH (f), + width != 0) + && (height = desired_row->visible_height, + height > 0)) + { + int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y)); + + /* Internal border is drawn below the tool bar. */ + if (WINDOWP (f->tool_bar_window) + && w == XWINDOW (f->tool_bar_window)) + y -= width; + /* end copy from other terms */ + + BLOCK_INPUT; + if (!desired_row->full_width_p) + { + int x1 = WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (w) + + WINDOW_LEFT_FRINGE_WIDTH (w); + int x2 = WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (w) + + FRAME_PIXEL_WIDTH (f) - NS_SCROLL_BAR_WIDTH (f) + - WINDOW_RIGHT_FRINGE_WIDTH (w) + - FRAME_INTERNAL_BORDER_WIDTH (f); + ns_clear_frame_area (f, x1, y, width, height); + ns_clear_frame_area (f, x2, y, width, height); + } + UNBLOCK_INPUT; + } +} + + +static void +ns_shift_glyphs_for_insert (struct frame *f, + int x, int y, int width, int height, + int shift_by) +/* -------------------------------------------------------------------------- + 23: External (RIF): copy an area horizontally, don't worry about clearing src + -------------------------------------------------------------------------- */ +{ + NSRect srcRect = NSMakeRect (x, y, width, height); + NSRect dstRect = NSMakeRect (x+shift_by, y, width, height); + NSPoint dstOrigin = dstRect.origin; + + NSTRACE (ns_shift_glyphs_for_insert); + + ns_focus (f, &dstRect, 1); + NSCopyBits (0, srcRect, dstOrigin); + ns_unfocus (f); +} + + + +/* ========================================================================== + + Character encoding and metrics + + ========================================================================== */ + + +static inline void +ns_compute_glyph_string_overhangs (struct glyph_string *s) +/* -------------------------------------------------------------------------- + 23: External (RIF); compute left/right overhang of whole string and set in s + -------------------------------------------------------------------------- */ +{ + struct face *face = FACE_FROM_ID (s->f, s->first_glyph->face_id); + struct font *font = s->font; /*face->font; */ + + if (s->char2b) + { + struct font_metrics metrics; + unsigned int codes[2]; + codes[0] = *(s->char2b); + codes[1] = *(s->char2b + s->nchars - 1); + + font->driver->text_extents (font, codes, 2, &metrics); + s->left_overhang = -metrics.lbearing; + s->right_overhang = + metrics.rbearing > metrics.width ? metrics.rbearing - metrics.width : 0; + } + else + { + s->left_overhang = 0; + s->right_overhang = ((struct nsfont_info *)font)->ital ? + FONT_HEIGHT (font) * 0.2 : 0; + } +} + + + +/* ========================================================================== + + Fringe and cursor drawing + + ========================================================================== */ + + +extern int max_used_fringe_bitmap; +static void +ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row, + struct draw_fringe_bitmap_params *p) +/* -------------------------------------------------------------------------- + 23: External (RIF); fringe-related + -------------------------------------------------------------------------- */ +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + struct face *face = p->face; + int rowY; + static EmacsImage **bimgs = NULL; + static int nBimgs = 0; + /* NS-specific: move internal border inside fringe */ + int x = p->bx < 0 ? p->x : p->bx; + int wd = p->bx < 0 ? p->wd : p->nx; + BOOL fringeOnVeryLeft = + x - WINDOW_LEFT_SCROLL_BAR_COLS (w) * WINDOW_FRAME_COLUMN_WIDTH (w) + - FRAME_INTERNAL_BORDER_WIDTH (f) < 10; + BOOL fringeOnVeryRight = + FRAME_PIXEL_WIDTH (f) - x - wd - FRAME_INTERNAL_BORDER_WIDTH (f) + - WINDOW_RIGHT_SCROLL_BAR_COLS (w) * WINDOW_FRAME_COLUMN_WIDTH (w) < 10; + int xAdjust = FRAME_INTERNAL_BORDER_WIDTH (f) * + (fringeOnVeryLeft ? -1 : (fringeOnVeryRight ? 1 : 0)); + + /* grow bimgs if needed */ + if (nBimgs < max_used_fringe_bitmap) + { + EmacsImage **newBimgs = + xmalloc (max_used_fringe_bitmap * sizeof (EmacsImage *)); + bzero (newBimgs, max_used_fringe_bitmap * sizeof (EmacsImage *)); + + if (nBimgs) + { + bcopy (bimgs, newBimgs, nBimgs * sizeof (EmacsImage *)); + xfree (bimgs); + } + + bimgs = newBimgs; + nBimgs = max_used_fringe_bitmap; + } + + /* Must clip because of partially visible lines. */ + rowY = WINDOW_TO_FRAME_PIXEL_Y (w, row->y); + if (p->y < rowY) + { + /* Adjust position of "bottom aligned" bitmap on partially + visible last row. */ + int oldY = row->y; + int oldVH = row->visible_height; + row->visible_height = p->h; + row->y -= rowY - p->y; + ns_clip_to_row (w, row, -1, NULL); + row->y = oldY; + row->visible_height = oldVH; + } + else + ns_clip_to_row (w, row, -1, YES); + + if (p->bx >= 0 && !p->overlay_p) + { + int yAdjust = rowY - FRAME_INTERNAL_BORDER_WIDTH (f) < 5 ? + -FRAME_INTERNAL_BORDER_WIDTH (f) : 0; + int yIncr = FRAME_PIXEL_HEIGHT (f) - (p->by+yAdjust + p->ny) < 5 ? + FRAME_INTERNAL_BORDER_WIDTH (f) : 0; + if (yAdjust) + yIncr += FRAME_INTERNAL_BORDER_WIDTH (f); + NSRect r = NSMakeRect (p->bx+xAdjust, p->by+yAdjust, p->nx, p->ny+yIncr); + NSRectClip (r); + [ns_lookup_indexed_color(face->background, f) set]; + NSRectFill (r); + } + + if (p->which) + { + NSRect r = NSMakeRect (p->x+xAdjust, p->y, p->wd, p->h); + NSPoint pt = r.origin; + EmacsImage *img = bimgs[p->which - 1]; + + if (!img) + { + unsigned short *bits = p->bits + p->dh; + int len = 8 * p->h/8; + int i; + unsigned char *cbits = xmalloc (len); + + for (i =0; i<len; i++) + cbits[i] = ~(bits[i] & 0xff); + img = [[EmacsImage alloc] initFromXBM: cbits width: 8 height: p->h + flip: NO]; + bimgs[p->which - 1] = img; + xfree (cbits); + } + + NSRectClip (r); + /* Since we composite the bitmap instead of just blitting it, we need + to erase the whole background. */ + [ns_lookup_indexed_color(face->background, f) set]; + NSRectFill (r); + pt.y += p->h; + [img setXBMColor: ns_lookup_indexed_color(face->foreground, f)]; + [img compositeToPoint: pt operation: NSCompositeSourceOver]; + } + ns_unfocus (f); +} + + +void +ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, + int x, int y, int cursor_type, int cursor_width, + int on_p, int active_p) +/* -------------------------------------------------------------------------- + External call (RIF): draw cursor + -------------------------------------------------------------------------- */ +{ + NSRect r, s; + int fx, fy, h; + struct frame *f = WINDOW_XFRAME (w); + struct glyph *phys_cursor_glyph; + int overspill; + unsigned char drawGlyph = 0, cursorType, oldCursorType; + + NSTRACE (dumpcursor); + + if (!on_p) + return; + + w->phys_cursor_type = cursor_type; + w->phys_cursor_on_p = 1; + + if (cursor_type == NO_CURSOR) + { + w->phys_cursor_width = 0; + return; + } + + if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL) + { + if (glyph_row->exact_window_width_line_p + && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA]) + { + glyph_row->cursor_in_fringe_p = 1; + draw_fringe_bitmap (w, glyph_row, 0); + } + return; + } + + get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h); + + r.origin.x = fx, r.origin.y = fy; + r.size.height = h; + r.size.width = w->phys_cursor_width; + + /* PENDING: if we overwrite the internal border area, it does not get erased; + fix by truncating cursor, but better would be to erase properly */ + overspill = r.origin.x + r.size.width - + WINDOW_TEXT_TO_FRAME_PIXEL_X (w, WINDOW_BOX_RIGHT_EDGE_X (w) + - WINDOW_TOTAL_FRINGE_WIDTH (w) - FRAME_INTERNAL_BORDER_WIDTH (f)); + if (overspill > 0) + r.size.width -= overspill; + + /* PENDING: 23: use emacs stored f->cursor_type instead of ns-specific */ + oldCursorType = FRAME_CURSOR (f); + cursorType = FRAME_CURSOR (f) = FRAME_NEW_CURSOR (f); + f->output_data.ns->current_cursor_color = + f->output_data.ns->desired_cursor_color; + + /* PENDING: only needed in rare cases with last-resort font in HELLO.. + should we do this more efficiently? */ + ns_clip_to_row (w, glyph_row, -1, NULL); +/* ns_focus (f, &r, 1); */ + + if (FRAME_LAST_INACTIVE (f)) + { + /* previously hollow box; clear entire area */ + [FRAME_BACKGROUND_COLOR (f) set]; + NSRectFill (r); + drawGlyph = 1; + FRAME_LAST_INACTIVE (f) = NO; + } + + /* prepare to draw */ + if (cursorType == no_highlight || cursor_type == NO_CURSOR) + { + /* clearing for blink: erase the cursor itself */ + [FRAME_BACKGROUND_COLOR (f) set]; + cursorType = oldCursorType; /* just clear what we had before */ + } + else + [FRAME_CURSOR_COLOR (f) set]; + + if (!active_p) + { + /* inactive window: ignore what we just set and use a hollow box */ + cursorType = hollow_box; + [FRAME_CURSOR_COLOR (f) set]; + } + + switch (cursorType) + { + case no_highlight: + break; + case filled_box: + NSRectFill (r); + drawGlyph = 1; + break; + case hollow_box: + NSRectFill (r); + [FRAME_BACKGROUND_COLOR (f) set]; + NSRectFill (NSInsetRect (r, 1, 1)); + [FRAME_CURSOR_COLOR (f) set]; + drawGlyph = 1; + break; + case underscore: + s = r; + s.origin.y += lrint (0.75 * s.size.height); + s.size.height = lrint (s.size.height * 0.25); + NSRectFill (s); + break; + case bar: + s = r; + s.size.width = 1; + NSRectFill (s); + break; + } + ns_unfocus (f); + + /* if needed, draw the character under the cursor */ + if (drawGlyph) + draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); +} + + +static void +ns_draw_vertical_window_border (struct window *w, int x, int y0, int y1) +/* -------------------------------------------------------------------------- + External (RIF): Draw a vertical line. + -------------------------------------------------------------------------- */ +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + struct face *face; + NSRect r = NSMakeRect (x, y0, 2, y1-y0); + + face = FACE_FROM_ID (f, VERTICAL_BORDER_FACE_ID); + if (face) + [ns_lookup_indexed_color(face->foreground, f) set]; + + ns_focus (f, &r, 1); + NSDrawGroove (r, r); + ns_unfocus (f); +} + + +void +show_hourglass (struct atimer *timer) +{ + if (hourglass_shown_p) + return; + + BLOCK_INPUT; + + /*PENDING: add NSProgressIndicator to selected frame (see macfns.c) */ + + hourglass_shown_p = 1; + UNBLOCK_INPUT; +} + + +void +hide_hourglass () +{ + if (!hourglass_shown_p) + return; + + /*PENDING: remove NSProgressIndicator from all frames */ + + hourglass_shown_p = 0; + UNBLOCK_INPUT; +} + + + +/* ========================================================================== + + Glyph drawing operations + + ========================================================================== */ + + +static inline NSRect +/* -------------------------------------------------------------------------- + Under NS we draw internal borders inside fringes, and want full-width + rendering to go all the way to edge. This function makes that correction. + -------------------------------------------------------------------------- */ +ns_fix_rect_ibw (NSRect r, int fibw, int frame_pixel_width) +{ + if (r.origin.y <= fibw+1) + { + r.size.height += r.origin.y; + r.origin.y = 0; + } + if (r.origin.x <= fibw+1) + { + r.size.width += r.origin.x; + r.origin.x = 0; + } + if (frame_pixel_width - (r.origin.x+r.size.width) <= fibw+1) + r.size.width += fibw; + + return r; +} + + +static int +ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr) +/* -------------------------------------------------------------------------- + Wrapper utility to account for internal border width on full-width lines, + and allow top full-width rows to hit the frame top. nr should be pointer + to two successive NSRects. Number of rects actually used is returned. + -------------------------------------------------------------------------- */ +{ + int n = get_glyph_string_clip_rects (s, nr, 2); + if (s->row->full_width_p) + { + *nr = ns_fix_rect_ibw (*nr, FRAME_INTERNAL_BORDER_WIDTH (s->f), + FRAME_PIXEL_WIDTH (s->f)); + if (n == 2) + *nr = ns_fix_rect_ibw (*(nr+1), FRAME_INTERNAL_BORDER_WIDTH (s->f), + FRAME_PIXEL_WIDTH (s->f)); + } + return n; +} + + +static void +ns_draw_box (NSRect r, float thickness, NSColor *col, char left_p, char right_p) +/* -------------------------------------------------------------------------- + Draw an unfilled rect inside r, optionally leaving left and/or right open. + Note we can't just use an NSDrawRect command, because of the possibility + of some sides not being drawn, and because the rect will be filled. + -------------------------------------------------------------------------- */ +{ + NSRect s = r; + [col set]; + + /* top, bottom */ + s.size.height = thickness; + NSRectFill (s); + s.origin.y += r.size.height - thickness; + NSRectFill (s); + + s.size.height = r.size.height; + s.origin.y = r.origin.y; + + /* left, right (optional) */ + s.size.width = thickness; + if (left_p) + NSRectFill (s); + if (right_p) + { + s.origin.x += r.size.width - thickness; + NSRectFill (s); + } +} + + +static void +ns_draw_relief (NSRect r, int thickness, char raised_p, + char top_p, char bottom_p, char left_p, char right_p, + struct glyph_string *s) +/* -------------------------------------------------------------------------- + Draw a relief rect inside r, optionally leaving some sides open. + Note we can't just use an NSDrawBezel command, because of the possibility + of some sides not being drawn, and because the rect will be filled. + -------------------------------------------------------------------------- */ +{ + static NSColor *baseCol = nil, *lightCol = nil, *darkCol = nil; + NSColor *newBaseCol = nil; + NSRect sr = r; + + NSTRACE (ns_draw_relief); + + /* set up colors */ + + if (s->face->use_box_color_for_shadows_p) + { + newBaseCol = ns_lookup_indexed_color (s->face->box_color, s->f); + } +/* else if (s->first_glyph->type == IMAGE_GLYPH + && s->img->pixmap + && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0)) + { + newBaseCol = IMAGE_BACKGROUND (s->img, s->f, 0); + } */ + else + { + newBaseCol = ns_lookup_indexed_color (s->face->background, s->f); + } + + if (newBaseCol == nil) + newBaseCol = [NSColor grayColor]; + + if (newBaseCol != baseCol) /* PENDING: better check */ + { + [baseCol release]; + baseCol = [newBaseCol retain]; + [lightCol release]; + lightCol = [[baseCol highlightWithLevel: 0.2] retain]; + [darkCol release]; + darkCol = [[baseCol shadowWithLevel: 0.3] retain]; + } + + [(raised_p ? lightCol : darkCol) set]; + + /* TODO: mitering. Using NSBezierPath doesn't work because of color switch. */ + + /* top */ + sr.size.height = thickness; + if (top_p) NSRectFill (sr); + + /* left */ + sr.size.height = r.size.height; + sr.size.width = thickness; + if (left_p) NSRectFill (sr); + + [(raised_p ? darkCol : lightCol) set]; + + /* bottom */ + sr.size.width = r.size.width; + sr.size.height = thickness; + sr.origin.y += r.size.height - thickness; + if (bottom_p) NSRectFill (sr); + + /* right */ + sr.size.height = r.size.height; + sr.origin.y = r.origin.y; + sr.size.width = thickness; + sr.origin.x += r.size.width - thickness; + if (right_p) NSRectFill (sr); +} + + +static void +ns_dumpglyphs_box_or_relief (struct glyph_string *s) +/* -------------------------------------------------------------------------- + Function modeled after x_draw_glyph_string_box (). + Sets up parameters for drawing. + -------------------------------------------------------------------------- */ +{ + int right_x, last_x; + char left_p, right_p; + struct glyph *last_glyph; + NSRect r; + int thickness; + struct face *face; + + if (s->hl == DRAW_MOUSE_FACE) + { + face = FACE_FROM_ID + (s->f, FRAME_NS_DISPLAY_INFO (s->f)->mouse_face_face_id); + if (!face) + face = FACE_FROM_ID (s->f, MOUSE_FACE_ID); + } + else + face = s->face; + + thickness = face->box_line_width; + + NSTRACE (ns_dumpglyphs_box_or_relief); + + last_x = ((s->row->full_width_p && !s->w->pseudo_window_p) + ? WINDOW_RIGHT_EDGE_X (s->w) + : window_box_right (s->w, s->area)); + last_glyph = (s->cmp || s->img + ? s->first_glyph : s->first_glyph + s->nchars-1); + + right_x = ((s->row->full_width_p && s->extends_to_end_of_line_p + ? last_x - 1 : min (last_x, s->x + s->background_width) - 1)); + + left_p = (s->first_glyph->left_box_line_p + || (s->hl == DRAW_MOUSE_FACE + && (s->prev == NULL || s->prev->hl != s->hl))); + right_p = (last_glyph->right_box_line_p + || (s->hl == DRAW_MOUSE_FACE + && (s->next == NULL || s->next->hl != s->hl))); + + r = NSMakeRect (s->x, s->y, right_x - s->x + 1, s->height); + + /* expand full-width row over internal borders */ + if (s->row->full_width_p) + r = ns_fix_rect_ibw (r, FRAME_INTERNAL_BORDER_WIDTH (s->f), + FRAME_PIXEL_WIDTH (s->f)); + + if (s->face->box == FACE_SIMPLE_BOX) + { + xassert (s->face->box_color != nil); + ns_draw_box (r, abs (thickness), + ns_lookup_indexed_color (face->box_color, s->f), + left_p, right_p); + } + else + { + ns_draw_relief (r, abs (thickness), s->face->box == FACE_RAISED_BOX, + 1, 1, left_p, right_p, s); + } +} + + +static void +ns_maybe_dumpglyphs_background (struct glyph_string *s, char force_p) +/* -------------------------------------------------------------------------- + Modeled after x_draw_glyph_string_background, which draws BG in + certain cases. Others are left to the text rendering routine. + -------------------------------------------------------------------------- */ +{ + NSTRACE (ns_maybe_dumpglyphs_background); + + if (!s->background_filled_p/* || s->hl == DRAW_MOUSE_FACE*/) + { + int box_line_width = max (s->face->box_line_width, 0); + if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width + || s->font_not_found_p || s->extends_to_end_of_line_p || force_p) + { + struct face *face; + if (s->hl == DRAW_MOUSE_FACE) + { + face = FACE_FROM_ID + (s->f, FRAME_NS_DISPLAY_INFO (s->f)->mouse_face_face_id); + if (!face) + face = FACE_FROM_ID (s->f, MOUSE_FACE_ID); + } + else + face = FACE_FROM_ID (s->f, s->first_glyph->face_id); + if (!face->stipple) + [(NS_FACE_BACKGROUND (face) != nil + ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) + : FRAME_BACKGROUND_COLOR (s->f)) set]; + else + { + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (s->f); + [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set]; + } + + if (s->hl != DRAW_CURSOR) + { + NSRect r = NSMakeRect (s->x, s->y + box_line_width, + s->background_width, + s->height-2*box_line_width); + + /* expand full-width row over internal borders */ + if (s->row->full_width_p) + { + int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f); + if (r.origin.y <= fibw+1 + box_line_width) + { + r.size.height += r.origin.y; + r.origin.y = 0; + } + if (r.origin.x <= fibw+1) + { + r.size.width += 2*r.origin.x; + r.origin.x = 0; + } + if (FRAME_PIXEL_WIDTH (s->f) - (r.origin.x + r.size.width) + <= fibw+1) + r.size.width += fibw; + } + + NSRectFill (r); + } + + s->background_filled_p = 1; + } + } +} + + +static void +ns_dumpglyphs_image (struct glyph_string *s, NSRect r) +/* -------------------------------------------------------------------------- + Renders an image and associated borders. + -------------------------------------------------------------------------- */ +{ + EmacsImage *img = s->img->pixmap; + int box_line_vwidth = max (s->face->box_line_width, 0); + int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice); + int bg_x, bg_y, bg_height; + int th; + char raised_p; + NSRect br; + + NSTRACE (ns_dumpglyphs_image); + + if (s->face->box != FACE_NO_BOX + && s->first_glyph->left_box_line_p && s->slice.x == 0) + x += abs (s->face->box_line_width); + + bg_x = x; + bg_y = s->slice.y == 0 ? s->y : s->y + box_line_vwidth; + bg_height = s->height; + /* other terms have this, but was causing problems w/tabbar mode */ + /* - 2 * box_line_vwidth; */ + + if (s->slice.x == 0) x += s->img->hmargin; + if (s->slice.y == 0) y += s->img->vmargin; + + /* Draw BG: if we need larger area than image itself cleared, do that, + otherwise, since we composite the image under NS (instead of mucking + with its background color), we must clear just the image area. */ + [ns_lookup_indexed_color (NS_FACE_BACKGROUND + (FACE_FROM_ID (s->f, s->first_glyph->face_id)), s->f) set]; + + if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin + || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width) + { + br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height); + s->background_filled_p = 1; + } + else + { + br = NSMakeRect (x, y, s->slice.width, s->slice.height); + } + + /* expand full-width row over internal borders */ + if (s->row->full_width_p) + { + int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f); + if (br.origin.y <= fibw+1 + box_line_vwidth) + { + br.size.height += br.origin.y; + br.origin.y = 0; + } + if (br.origin.x <= fibw+1 + box_line_vwidth) + { + br.size.width += br.origin.x; + br.origin.x = 0; + } + if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width) <= fibw+1) + br.size.width += fibw; + } + + NSRectFill (br); + + /* Draw the image.. do we need to draw placeholder if img ==nil? */ + if (img != nil) + [img compositeToPoint: NSMakePoint (x, y + s->slice.height) + operation: NSCompositeSourceOver]; + + /* Draw relief, if requested */ + if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN) + { + if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED) + { + th = tool_bar_button_relief >= 0 ? + tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF; + raised_p = (s->hl == DRAW_IMAGE_RAISED); + } + else + { + th = abs (s->img->relief); + raised_p = (s->img->relief > 0); + } + + r.origin.x = x - th; + r.origin.y = y - th; + r.size.width = s->slice.width + 2*th-1; + r.size.height = s->slice.height + 2*th-1; + ns_draw_relief (r, th, raised_p, + s->slice.y == 0, + s->slice.y + s->slice.height == s->img->height, + s->slice.x == 0, + s->slice.x + s->slice.width == s->img->width, s); + } +} + + +static void +ns_draw_glyph_string (struct glyph_string *s) +/* -------------------------------------------------------------------------- + External (RIF): Main draw-text call. + -------------------------------------------------------------------------- */ +{ + /*PENDING (optimize): focus for box and contents draw */ + NSRect r[2]; + int n; + char box_drawn_p = 0; + + NSTRACE (ns_draw_glyph_string); + + if (s->next && s->right_overhang && !s->for_overlaps && s->hl != DRAW_CURSOR) + { + xassert (s->next->img == NULL); + n = ns_get_glyph_string_clip_rect (s->next, r); + ns_focus (s->f, r, n); + ns_maybe_dumpglyphs_background (s->next, 1); + ns_unfocus (s->f); + } + + if (!s->for_overlaps && s->face->box != FACE_NO_BOX + && (s->first_glyph->type == CHAR_GLYPH + || s->first_glyph->type == COMPOSITE_GLYPH)) + { + n = ns_get_glyph_string_clip_rect (s, r); + ns_focus (s->f, r, n); + ns_maybe_dumpglyphs_background (s, 1); + ns_dumpglyphs_box_or_relief (s); + ns_unfocus (s->f); + box_drawn_p = 1; + } + + switch (s->first_glyph->type) + { + + case IMAGE_GLYPH: + n = ns_get_glyph_string_clip_rect (s, r); + ns_focus (s->f, r, n); + ns_dumpglyphs_image (s, r[0]); + ns_unfocus (s->f); + break; + + case STRETCH_GLYPH: + if (!s->background_filled_p) + { + *r = NSMakeRect (s->x, s->y, s->background_width, s->height); + + if (!s->row->full_width_p) + { + /* truncate to avoid overwriting fringe and/or scrollbar */ + int overrun = max (0, (s->x + s->background_width) + - (WINDOW_BOX_RIGHT_EDGE_X (s->w) + - WINDOW_RIGHT_FRINGE_WIDTH (s->w))); + r[0].size.width -= overrun; + + /* PENDING: Try to work between problem where a stretch glyph on + a partially-visible bottom row will clear part of the + modeline, and another where list-buffers headers and similar + rows erroneously have visible_height set to 0. Not sure + where this is coming from as other terms seem not to show. */ + r[0].size.height = min (s->height, s->row->visible_height); + } + + /* expand full-width rows over internal borders */ + else + { + r[0] = ns_fix_rect_ibw (r[0], FRAME_INTERNAL_BORDER_WIDTH (s->f), + FRAME_PIXEL_WIDTH (s->f)); + } + + /* NOTE: under NS this is NOT used to draw cursors, but we must avoid + overwriting cursor (usually when cursor on a tab) */ + if (s->hl == DRAW_CURSOR) + { + r[0].origin.x += s->width; + r[0].size.width -= s->width; + } + + ns_focus (s->f, r, 1); + [ns_lookup_indexed_color (NS_FACE_BACKGROUND + (FACE_FROM_ID (s->f, s->first_glyph->face_id)), s->f) set]; + NSRectFill (r[0]); + ns_unfocus (s->f); + s->background_filled_p = 1; + } + break; + + case CHAR_GLYPH: + case COMPOSITE_GLYPH: + n = ns_get_glyph_string_clip_rect (s, r); + ns_focus (s->f, r, n); + + if (s->for_overlaps || s->gidx > 0) + s->background_filled_p = 1; + else /* 1 */ + ns_maybe_dumpglyphs_background + (s, s->first_glyph->type == COMPOSITE_GLYPH); + + ns_tmp_flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR : + (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE : + (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND : + NS_DUMPGLYPH_NORMAL)); + ns_tmp_font = (struct nsfont_info *)s->face->font; + if (ns_tmp_font == ~0 || ns_tmp_font == NULL) + ns_tmp_font = FRAME_FONT (s->f); + + ns_tmp_font->font.driver->draw + (s, 0, s->nchars, s->x, s->y, + (ns_tmp_flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p) + || ns_tmp_flags == NS_DUMPGLYPH_MOUSEFACE); + + ns_unfocus (s->f); + break; + + default: + abort (); + } + + /* Draw box if not done already. */ + if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX) + { + n = ns_get_glyph_string_clip_rect (s, r); + ns_focus (s->f, r, n); + ns_dumpglyphs_box_or_relief (s); + ns_unfocus (s->f); + } + +} + + + +/* ========================================================================== + + Event loop + + ========================================================================== */ + + +static void +ns_send_appdefined (int value) +/* -------------------------------------------------------------------------- + Internal: post an appdefined event which EmacsApp-sendEvent will + recognize and take as a command to halt the event loop. + -------------------------------------------------------------------------- */ +{ + /*NSTRACE (ns_send_appdefined); */ + + /* Only post this event if we haven't already posted one. This will end + the [NXApp run] main loop after having processed all events queued at + this moment. */ + if (send_appdefined) + { + NSEvent *nxev; + + /* We only need one NX_APPDEFINED event to stop NXApp from running. */ + send_appdefined = NO; + + /* Don't need wakeup timer any more */ + if (timed_entry) + { + [timed_entry invalidate]; + [timed_entry release]; + timed_entry = nil; + } + + /* Ditto for file descriptor poller */ + if (fd_entry) + { + [fd_entry invalidate]; + [fd_entry release]; + fd_entry = nil; + } + + nxev = [NSEvent otherEventWithType: NSApplicationDefined + location: NSMakePoint (0, 0) + modifierFlags: 0 + timestamp: 0 + windowNumber: [[NSApp mainWindow] windowNumber] + context: [NSApp context] + subtype: 0 + data1: value + data2: 0]; + + /* Post an application defined event on the event queue. When this is + received the [NXApp run] will return, thus having processed all + events which are currently queued. */ + [NSApp postEvent: nxev atStart: NO]; + } +} + + +static int +ns_read_socket (struct terminal *terminal, int expected, + struct input_event *hold_quit) +/* -------------------------------------------------------------------------- + External (hook): Post an event to ourself and keep reading events until + we read it back again. In effect process all events which were waiting. + 23: Now we have to manage the event buffer ourselves. + -------------------------------------------------------------------------- */ +{ + struct input_event ev; + int nevents; + static NSDate *lastCheck = nil; +/* NSTRACE (ns_read_socket); */ + + if (interrupt_input_blocked) + { + interrupt_input_pending = 1; + return -1; + } + + interrupt_input_pending = 0; + BLOCK_INPUT; + +#ifdef COCOA_EXPERIMENTAL_CTRL_G + /* causes Feval to abort; unclear on why this isn't in calling code */ + ++handling_signal; +#endif + + n_emacs_events_pending = 0; + EVENT_INIT (ev); + emacs_event = &ev; + q_event_ptr = hold_quit; + + /* we manage autorelease pools by allocate/reallocate each time around + the loop; strict nesting is occasionally violated but seems not to + matter.. earlier methods using full nesting caused major memory leaks */ + [outerpool release]; + outerpool = [[NSAutoreleasePool alloc] init]; + + /* If have pending open-file requests, attend to the next one of those. */ + if (ns_pending_files && [ns_pending_files count] != 0 + && [NSApp openFile: [ns_pending_files objectAtIndex: 0]]) + { + [ns_pending_files removeObjectAtIndex: 0]; + } + /* Deal with pending service requests. */ + else if (ns_pending_service_names && [ns_pending_service_names count] != 0 + && [NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0] + withArg: [ns_pending_service_args objectAtIndex: 0]]) + { + [ns_pending_service_names removeObjectAtIndex: 0]; + [ns_pending_service_args removeObjectAtIndex: 0]; + } + else + { + /* Run and wait for events. We must always send one NX_APPDEFINED event + to ourself, otherwise [NXApp run] will never exit. */ + send_appdefined = YES; + + /*PENDING: from termhooks.h: */ + /* XXX Please note that a non-zero value of EXPECTED only means that + there is available input on at least one of the currently opened + terminal devices -- but not necessarily on this device. + Therefore, in most cases EXPECTED should be simply ignored. */ + /* However, if in ns_select, this is called from gobble_input, which + appears to set it correctly for our purposes, and always assuming + !expected causes 100% CPU usage. */ + if (!inNsSelect || !expected) + { + /* Post an application defined event on the event queue. When this is + received the [NXApp run] will return, thus having processed all + events which are currently queued, if any. */ + ns_send_appdefined (-1); + } + + [NSApp run]; + } + + nevents = n_emacs_events_pending; + n_emacs_events_pending = 0; + emacs_event = q_event_ptr = NULL; + +#ifdef COCOA_EXPERIMENTAL_CTRL_G + --handling_signal; +#endif + UNBLOCK_INPUT; + return nevents; +} + + +int +ns_select (int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) +/* -------------------------------------------------------------------------- + Replacement for select, checking for events + -------------------------------------------------------------------------- */ +{ + int result; + double time; + NSEvent *ev; +/* NSTRACE (ns_select); */ + + if (NSApp == nil /* || ([NSApp isActive] == NO && + [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil + inMode:NSDefaultRunLoopMode dequeue:NO] == nil) */) + return select (nfds, readfds, writefds, exceptfds, timeout); + + /* Save file descriptor set, which gets overwritten in calls to select () + Note, this is called from process.c, and only readfds is ever set */ + if (readfds) + { + memcpy (&select_readfds, readfds, sizeof (fd_set)); + select_nfds = nfds; + } + else + select_nfds = 0; + + /* Try an initial select for pending data on input files */ + select_timeout.tv_sec = select_timeout.tv_usec = 0; + result = select (nfds, readfds, writefds, exceptfds, &select_timeout); + if (result) + return result; + + /* if (!timeout || timed_entry || fd_entry) + fprintf (stderr, "assertion failed: timeout null or timed_entry/fd_entry non-null in ns_select\n"); */ + + /* set a timeout and run the main AppKit event loop while continuing + to monitor the files */ + time = ((double) timeout->tv_sec) + ((double) timeout->tv_usec)/1000000.0; + timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time + target: NSApp + selector: @selector (timeout_handler:) + userInfo: 0 + repeats: YES] /* for safe removal */ + retain]; + + /* set a periodic task to try the select () again */ + fd_entry = [[NSTimer scheduledTimerWithTimeInterval: 0.1 + target: NSApp + selector: @selector (fd_handler:) + userInfo: 0 + repeats: YES] + retain]; + + if (!NILP (ns_cursor_blink_mode) && !cursor_blink_entry) + { + if (!NUMBERP (ns_cursor_blink_rate)) + ns_cursor_blink_rate = make_float (0.5); + cursor_blink_entry = [[NSTimer + scheduledTimerWithTimeInterval: XFLOATINT (ns_cursor_blink_rate) + target: NSApp + selector: @selector (cursor_blink_handler:) + userInfo: 0 + repeats: YES] + retain]; + } + else if (NILP (ns_cursor_blink_mode) && cursor_blink_entry) + { + if (NUMBERP (ns_cursor_blink_rate)) + ns_cursor_blink_rate = Qnil; + struct ns_display_info *dpyinfo = ns_display_list; /* HACK */ + [cursor_blink_entry invalidate]; + [cursor_blink_entry release]; + cursor_blink_entry = 0; + if (dpyinfo->ns_highlight_frame) + { + Lisp_Object tem = + get_frame_param (dpyinfo->ns_highlight_frame, Qcursor_type); + dpyinfo->ns_highlight_frame->output_data.ns->desired_cursor = + ns_lisp_to_cursor_type (tem); + } + } + + /* Let Application dispatch events until it receives an event of the type + NX_APPDEFINED, which should only be sent by timeout_handler. */ + inNsSelect = 1; + gobble_input (timeout ? 1 : 0); + ev = last_appdefined_event; + inNsSelect = 0; + + if (ev) + { + int t; + if ([ev type] != NSApplicationDefined) + abort (); + + t = [ev data1]; + last_appdefined_event = 0; + + if (t == -2) + { + /* The NX_APPDEFINED event we received was a timeout. */ + return 0; + } + else if (t == -1) + { + /* The NX_APPDEFINED event we received was the result of + at least one real input event arriving. */ + errno = EINTR; + return -1; + } + else + { + /* Received back from select () in fd_handler; copy the results */ + if (readfds) + memcpy (readfds, &select_readfds, sizeof (fd_set)); + return t; + } + } + /* never reached, shut compiler up */ + return 0; +} + + + +/* ========================================================================== + + Scrollbar handling + + ========================================================================== */ + + +static void +ns_set_vertical_scroll_bar (struct window *window, + int portion, int whole, int position) +/* -------------------------------------------------------------------------- + External (hook): Update or add scrollbar + -------------------------------------------------------------------------- */ +{ + Lisp_Object win; + NSRect r, v; + struct frame *f = XFRAME (WINDOW_FRAME (window)); + EmacsView *view = FRAME_NS_VIEW (f); + int window_y, window_height; + BOOL barOnVeryLeft, barOnVeryRight; + int top, left, height, width, sb_width, sb_left; + EmacsScroller *bar; +static int count = 0; + + /* optimization; display engine sends WAY too many of these.. */ + if (!NILP (window->vertical_scroll_bar)) + { + bar = XNS_SCROLL_BAR (window->vertical_scroll_bar); + if ([bar checkSamePosition: position portion: portion whole: whole]) + { + if (view->scrollbarsNeedingUpdate == 0) + { + if (!windows_or_buffers_changed) + return; + } + else + view->scrollbarsNeedingUpdate--; + } + } + + NSTRACE (ns_set_vertical_scroll_bar); + + /* Get dimensions. */ + window_box (window, -1, 0, &window_y, 0, &window_height); + top = window_y; + height = window_height; + width = WINDOW_CONFIG_SCROLL_BAR_COLS (window) * FRAME_COLUMN_WIDTH (f); + left = WINDOW_SCROLL_BAR_AREA_X (window); + + if (top < 5) /* top scrollbar adjustment */ + { + top -= FRAME_INTERNAL_BORDER_WIDTH (f); + height += FRAME_INTERNAL_BORDER_WIDTH (f); + } + + /* allow for displaying a skinnier scrollbar than char area allotted */ + sb_width = (WINDOW_CONFIG_SCROLL_BAR_WIDTH (window) > 0) ? + WINDOW_CONFIG_SCROLL_BAR_WIDTH (window) : width; + + barOnVeryLeft = left < 5; + barOnVeryRight = FRAME_PIXEL_WIDTH (f) - left - width < 5; + sb_left = left + FRAME_INTERNAL_BORDER_WIDTH (f) + * (barOnVeryLeft ? -1 : (barOnVeryRight ? 1 : 0)); + + r = NSMakeRect (sb_left, top, sb_width, height); + /* the parent view is flipped, so we need to flip y value */ + v = [view frame]; + r.origin.y = (v.size.height - r.size.height - r.origin.y); + + XSETWINDOW (win, window); + BLOCK_INPUT; + + /* we want at least 5 lines to display a scrollbar */ + if (WINDOW_TOTAL_LINES (window) < 5) + { + if (!NILP (window->vertical_scroll_bar)) + { + bar = XNS_SCROLL_BAR (window->vertical_scroll_bar); + [bar removeFromSuperview]; + window->vertical_scroll_bar = Qnil; + } + ns_clear_frame_area (f, sb_left, top, width, height); + UNBLOCK_INPUT; + return; + } + + if (NILP (window->vertical_scroll_bar)) + { + ns_clear_frame_area (f, sb_left, top, width, height); + bar = [[EmacsScroller alloc] initFrame: r window: win]; + window->vertical_scroll_bar = make_save_value (bar, 0); + } + else + { + NSRect oldRect; + bar = XNS_SCROLL_BAR (window->vertical_scroll_bar); + oldRect = [bar frame]; + r.size.width = oldRect.size.width; + if (FRAME_LIVE_P (f) && !NSEqualRects (oldRect, r)) + { + if (oldRect.origin.x != r.origin.x) + ns_clear_frame_area (f, sb_left, top, width, height); + [bar setFrame: r]; + } + } + + [bar setPosition: position portion: portion whole: whole]; + UNBLOCK_INPUT; +} + + +static void +ns_condemn_scroll_bars (struct frame *f) +/* -------------------------------------------------------------------------- + External (hook): arrange for all frame's scrollbars to be removed + at next call to judge_scroll_bars, except for those redeemed. + -------------------------------------------------------------------------- */ +{ + int i; + id view; + NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews]; + + NSTRACE (ns_condemn_scroll_bars); + + for (i =[subviews count]-1; i >= 0; i--) + { + view = [subviews objectAtIndex: i]; + if ([view isKindOfClass: [EmacsScroller class]]) + [view condemn]; + } +} + + +static void +ns_redeem_scroll_bar (struct window *window) +/* -------------------------------------------------------------------------- + External (hook): arrange to spare this window's scrollbar + at next call to judge_scroll_bars. + -------------------------------------------------------------------------- */ +{ + id bar; + NSTRACE (ns_redeem_scroll_bar); + if (!NILP (window->vertical_scroll_bar)) + { + bar =XNS_SCROLL_BAR (window->vertical_scroll_bar); + [bar reprieve]; + } +} + + +static void +ns_judge_scroll_bars (struct frame *f) +/* -------------------------------------------------------------------------- + External (hook): destroy all scrollbars on frame that weren't + redeemed after call to condemn_scroll_bars. + -------------------------------------------------------------------------- */ +{ + int i; + id view; + NSArray *subviews = [[FRAME_NS_VIEW (f) superview] subviews]; + NSTRACE (ns_judge_scroll_bars); + for (i =[subviews count]-1; i >= 0; i--) + { + view = [subviews objectAtIndex: i]; + if (![view isKindOfClass: [EmacsScroller class]]) continue; + [view judge]; + } +} + + + +/* ========================================================================== + + Miscellaneous, mainly stubbed-out functions added in 23 + + ========================================================================== */ + + +void +x_wm_set_icon_position (struct frame *f, int icon_x, int icon_y) +{ +} + + + +/* ========================================================================== + + Initialization + + ========================================================================== */ + +static Lisp_Object ns_string_to_lispmod (char *s) +/* -------------------------------------------------------------------------- + Convert modifier name to lisp symbol + -------------------------------------------------------------------------- */ +{ + if (!strncmp (SDATA (SYMBOL_NAME (Qmeta)), s, 10)) + return Qmeta; + else if (!strncmp (SDATA (SYMBOL_NAME (Qsuper)), s, 10)) + return Qsuper; + else if (!strncmp (SDATA (SYMBOL_NAME (Qcontrol)), s, 10)) + return Qcontrol; + else if (!strncmp (SDATA (SYMBOL_NAME (Qalt)), s, 10)) + return Qalt; + else if (!strncmp (SDATA (SYMBOL_NAME (Qhyper)), s, 10)) + return Qhyper; + else if (!strncmp (SDATA (SYMBOL_NAME (Qnone)), s, 10)) + return Qnone; + else + return Qnil; +} + + +static Lisp_Object ns_mod_to_lisp (int m) +/* -------------------------------------------------------------------------- + Convert modifier code (see lisp.h) to lisp symbol + -------------------------------------------------------------------------- */ +{ + if (m == CHAR_META) + return Qmeta; + else if (m == CHAR_SUPER) + return Qsuper; + else if (m == CHAR_CTL) + return Qcontrol; + else if (m == CHAR_ALT) + return Qalt; + else if (m == CHAR_HYPER) + return Qhyper; + else /* if (m == 0) */ + return Qnone; +} + + +static void +ns_set_default_prefs () +/* -------------------------------------------------------------------------- + Initialize preference variables to defaults + -------------------------------------------------------------------------- */ +{ + ns_alternate_modifier = Qmeta; + ns_command_modifier = Qsuper; + ns_control_modifier = Qcontrol; + ns_function_modifier = Qnone; + ns_cursor_blink_rate = Qnil; + ns_cursor_blink_mode = Qnil; + ns_expand_space = make_float (0.0); + ns_antialias_text = YES; + ns_antialias_threshold = 10.0; + ns_use_qd_smoothing = NO; + ns_use_system_highlight_color = YES; +} + + +static void +ns_default (const char *parameter, Lisp_Object *result, + Lisp_Object yesval, Lisp_Object noval, + BOOL is_float, BOOL is_modstring) +/* -------------------------------------------------------------------------- + Check a parameter value in user's preferences + -------------------------------------------------------------------------- */ +{ + const char *value; + + if ( (value =[[[NSUserDefaults standardUserDefaults] + stringForKey: [NSString stringWithUTF8String: parameter]] + UTF8String]) ) + { + double f; + char *pos; + if (strcasecmp (value, "YES") == 0) + *result = yesval; + else if (strcasecmp (value, "NO") == 0) + *result = noval; + else if (is_float && (f = strtod (value, &pos), pos != value)) + *result = make_float (f); + else if (is_modstring && value) + *result = ns_string_to_lispmod (value); + else fprintf (stderr, + "Bad value for default \"%s\": \"%s\"\n", parameter, value); + } +} + + +void +ns_initialize_display_info (struct ns_display_info *dpyinfo) +/* -------------------------------------------------------------------------- + Initialize global info and storage for display. + -------------------------------------------------------------------------- */ +{ + NSScreen *screen = [NSScreen mainScreen]; + NSWindowDepth depth = [screen depth]; + + dpyinfo->width = [screen frame].size.width; + dpyinfo->height = [screen frame].size.height; + dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected */ + dpyinfo->resy = 72.27; + dpyinfo->color_p = ![NSDeviceWhiteColorSpace isEqualToString: + NSColorSpaceFromDepth (depth)] + && ![NSCalibratedWhiteColorSpace isEqualToString: + NSColorSpaceFromDepth (depth)]; + dpyinfo->n_planes = NSBitsPerPixelFromDepth (depth); + dpyinfo->image_cache = make_image_cache (); + dpyinfo->color_table = + (struct ns_color_table *)xmalloc (sizeof (struct ns_color_table)); + dpyinfo->color_table->colors = NULL; + dpyinfo->root_window = 42; /* a placeholder.. */ + + dpyinfo->mouse_face_mouse_frame = NULL; + dpyinfo->mouse_face_deferred_gc = 0; + dpyinfo->mouse_face_beg_row = dpyinfo->mouse_face_beg_col = -1; + dpyinfo->mouse_face_end_row = dpyinfo->mouse_face_end_col = -1; + dpyinfo->mouse_face_face_id = DEFAULT_FACE_ID; + dpyinfo->mouse_face_window = dpyinfo->mouse_face_overlay = Qnil; + dpyinfo->mouse_face_hidden = 0; + + dpyinfo->mouse_face_mouse_x = dpyinfo->mouse_face_mouse_y = 0; + dpyinfo->mouse_face_defer = 0; + + dpyinfo->ns_highlight_frame = dpyinfo->ns_focus_frame = NULL; + + dpyinfo->n_fonts = 0; + dpyinfo->smallest_font_height = 1; + dpyinfo->smallest_char_width = 1; +} + + +/* 23: Needed as new part of display engine; this and next define public + functions in this file (well, many of them, anyway). */ +/* x_... are generic versions in xdisp.c that we, and other terms, get away + with using despite presence in the "system dependent" redisplay + interface. In addition, many of the ns_ methods have code that is + shared with all terms, indicating need for further refactoring. */ +extern frame_parm_handler ns_frame_parm_handlers[]; +static struct redisplay_interface ns_redisplay_interface = +{ + ns_frame_parm_handlers, + x_produce_glyphs, /*generic OK */ + x_write_glyphs, /*generic OK */ + x_insert_glyphs, /*generic OK */ + x_clear_end_of_line, /*generic OK */ + ns_scroll_run, /*23 */ + ns_after_update_window_line, /*23: added */ + ns_update_window_begin, /*23: split from update_begin */ + ns_update_window_end, /*23: split from update_end */ + x_cursor_to, /*generic OK */ + ns_flush, + 0, /* flush_display_optional */ + x_clear_window_mouse_face, /*generic OK */ + x_get_glyph_overhangs, /*23: generic OK */ + x_fix_overlapping_area, /*generic OK */ + ns_draw_fringe_bitmap, /*23 */ + 0, /* define_fringe_bitmap */ /*PENDING: simplify ns_draw_fringe_bitmap? */ + 0, /* destroy_fringe_bitmap */ + ns_compute_glyph_string_overhangs, /*23 */ + ns_draw_glyph_string, /*23: interface to nsfont.m */ + ns_define_frame_cursor, /*23 */ + ns_clear_frame_area, /*23 */ + ns_draw_window_cursor, /*23: revamped ns_dumpcursor */ + ns_draw_vertical_window_border, + ns_shift_glyphs_for_insert +}; + + +static void +ns_delete_display (struct ns_display_info *dpyinfo) +{ + /*PENDING... */ +} + + +/* This function is called when the last frame on a display is deleted. */ +static void +ns_delete_terminal (struct terminal *terminal) +{ + struct ns_display_info *dpyinfo = terminal->display_info.ns; + int i; + + /* Protect against recursive calls. Fdelete_frame in + delete_terminal calls us back when it deletes our last frame. */ + if (!terminal->name) + return; + + BLOCK_INPUT; + + x_destroy_all_bitmaps (dpyinfo); + ns_delete_display (dpyinfo); + UNBLOCK_INPUT; +} + + +static struct terminal * +ns_create_terminal (struct ns_display_info *dpyinfo) +/* -------------------------------------------------------------------------- + Set up use of NS before we make the first connection. + -------------------------------------------------------------------------- */ +{ + struct terminal *terminal; + + NSTRACE (ns_create_terminal); + + terminal = create_terminal (); + + terminal->type = output_ns; + terminal->display_info.ns = dpyinfo; + dpyinfo->terminal = terminal; + + terminal->rif = &ns_redisplay_interface; + + terminal->clear_frame_hook = ns_clear_frame; + terminal->ins_del_lines_hook = 0; /* 23: vestigial? */ + terminal->delete_glyphs_hook = 0; /* 23: vestigial? */ + terminal->ring_bell_hook = ns_ring_bell; + terminal->reset_terminal_modes_hook = ns_reset_terminal_modes; + terminal->set_terminal_modes_hook = ns_set_terminal_modes; + terminal->update_begin_hook = ns_update_begin; + terminal->update_end_hook = ns_update_end; + terminal->set_terminal_window_hook = NULL; /* 23: vestigial? */ + terminal->read_socket_hook = ns_read_socket; + terminal->frame_up_to_date_hook = ns_frame_up_to_date; + terminal->mouse_position_hook = ns_mouse_position; + terminal->frame_rehighlight_hook = ns_frame_rehighlight; + terminal->frame_raise_lower_hook = ns_frame_raise_lower; + + terminal->fullscreen_hook = 0; /*XTfullscreen_hook;//23.50 */ + + terminal->set_vertical_scroll_bar_hook = ns_set_vertical_scroll_bar; + terminal->condemn_scroll_bars_hook = ns_condemn_scroll_bars; + terminal->redeem_scroll_bar_hook = ns_redeem_scroll_bar; + terminal->judge_scroll_bars_hook = ns_judge_scroll_bars; + + terminal->delete_frame_hook = x_destroy_window; + terminal->delete_terminal_hook = ns_delete_terminal; + + terminal->scroll_region_ok = 1; + terminal->char_ins_del_ok = 1; + terminal->line_ins_del_ok = 1; + terminal->fast_clear_end_of_line = 1; + terminal->memory_below_frame = 0; + + return terminal; +} + + +void +ns_initialize () +/* -------------------------------------------------------------------------- + Mainly vestigial under NS now that ns_create_terminal () does most things. + -------------------------------------------------------------------------- */ +{ + baud_rate = 38400; + Fset_input_interrupt_mode (Qt); +} + + +struct ns_display_info * +ns_term_init (Lisp_Object display_name) +/* -------------------------------------------------------------------------- + Start the Application and get things rolling. + -------------------------------------------------------------------------- */ +{ + extern Lisp_Object Fset_input_mode (Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object); + struct terminal *terminal; + struct ns_display_info *dpyinfo; + static int ns_initialized = 0; + Lisp_Object tmp; + + NSTRACE (ns_term_init); + + /* count object allocs (About, click icon); on OS X use ObjectAlloc tool */ + /*GSDebugAllocationActive (YES); */ + BLOCK_INPUT; +handling_signal = 0; + + if (!ns_initialized) + { + ns_initialize (); + ns_initialized = 1; + } + + ns_pending_files = [[NSMutableArray alloc] init]; + ns_pending_service_names = [[NSMutableArray alloc] init]; + ns_pending_service_args = [[NSMutableArray alloc] init]; + + /* Start app and create the main menu, window, view. + Needs to be here because ns_initialize_display_info () uses AppKit classes. + The view will then ask the NSApp to stop and return to Emacs. */ + [EmacsApp sharedApplication]; + if (NSApp == nil) + return NULL; + [NSApp setDelegate: NSApp]; + + /* debugging: log all notifications */ + /* [[NSNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector (logNotification:) + name: nil object: nil]; */ + + dpyinfo = (struct ns_display_info *)xmalloc (sizeof (struct ns_display_info)); + bzero (dpyinfo, sizeof (struct ns_display_info)); + + ns_initialize_display_info (dpyinfo); + terminal = ns_create_terminal (dpyinfo); + + terminal->kboard = (KBOARD *) xmalloc (sizeof (KBOARD)); + init_kboard (terminal->kboard); + terminal->kboard->Vwindow_system = Qns; + terminal->kboard->next_kboard = all_kboards; + all_kboards = terminal->kboard; + /* Don't let the initial kboard remain current longer than necessary. + That would cause problems if a file loaded on startup tries to + prompt in the mini-buffer. */ + if (current_kboard == initial_kboard) + current_kboard = terminal->kboard; + terminal->kboard->reference_count++; + + dpyinfo->next = ns_display_list; + ns_display_list = dpyinfo; + + /* Put it on ns_display_name_list */ + ns_display_name_list = Fcons (Fcons (display_name, Qnil), + ns_display_name_list); +/* ns_display_name_list = Fcons (Fcons (display_name, + Fcons (Qnil, dpyinfo->xrdb)), + ns_display_name_list); */ + dpyinfo->name_list_element = XCAR (ns_display_name_list); + + /* Set the name of the terminal. */ + terminal->name = (char *) xmalloc (SBYTES (display_name) + 1); + strncpy (terminal->name, SDATA (display_name), SBYTES (display_name)); + terminal->name[SBYTES (display_name)] = 0; + + UNBLOCK_INPUT; + + /* Read various user defaults. */ + ns_set_default_prefs (); + ns_default ("AlternateModifier", &ns_alternate_modifier, + Qnil, Qnil, NO, YES); + if (NILP (ns_alternate_modifier)) + ns_alternate_modifier = Qmeta; + ns_default ("CommandModifier", &ns_command_modifier, + Qnil, Qnil, NO, YES); + if (NILP (ns_command_modifier)) + ns_command_modifier = Qsuper; + ns_default ("ControlModifier", &ns_control_modifier, + Qnil, Qnil, NO, YES); + if (NILP (ns_control_modifier)) + ns_control_modifier = Qcontrol; + ns_default ("FunctionModifier", &ns_function_modifier, + Qnil, Qnil, NO, YES); + if (NILP (ns_function_modifier)) + ns_function_modifier = Qnone; + ns_default ("CursorBlinkRate", &ns_cursor_blink_rate, + make_float (0.5), Qnil, YES, NO); + if (NUMBERP (ns_cursor_blink_rate)) + ns_cursor_blink_mode = Qt; + ns_default ("ExpandSpace", &ns_expand_space, + make_float (0.5), make_float (0.0), YES, NO); + ns_default ("GSFontAntiAlias", &ns_antialias_text, + YES, NO, NO, NO); + tmp = Qnil; + ns_default ("AppleAntiAliasingThreshold", &tmp, + make_float (10.0), make_float (6.0), YES, NO); + ns_antialias_threshold = NILP (tmp) ? 10.0 : XFLOATINT (tmp); + ns_default ("UseQuickdrawSmoothing", &ns_use_qd_smoothing, + YES, NO, NO, NO); + ns_default ("UseSystemHighlightColor", &ns_use_system_highlight_color, + YES, NO, NO, NO); + if (ns_use_system_highlight_color == YES) + { + ns_selection_color = [[NSUserDefaults standardUserDefaults] + stringForKey: @"AppleHighlightColor"]; + if (ns_selection_color == nil) + ns_selection_color = NS_SELECTION_COLOR_DEFAULT; + } + else + ns_selection_color = NS_SELECTION_COLOR_DEFAULT; + + { + id cl; + Lisp_Object tem, tem1; + extern Lisp_Object Vsource_directory; + + cl = [NSColorList colorListNamed: @"Emacs"]; + + if ( cl == nil ) + { + /* first try data_dir, then invocation-dir + and finally source-directory/etc */ + tem1 = tem = + Fexpand_file_name (build_string ("Emacs.clr"), Vdata_directory); + if (NILP (Ffile_exists_p (tem))) + { + tem = Fexpand_file_name (build_string ("Emacs.clr"), + Vinvocation_directory); + if (NILP (Ffile_exists_p (tem))) + { + Lisp_Object newdir = + Fexpand_file_name (build_string ("etc/"), + Vsource_directory); + tem = Fexpand_file_name (build_string ("Emacs.clr"), + newdir); + } + } + + cl = [[NSColorList alloc] + initWithName: @"Emacs" + fromFile: [NSString stringWithCString: XSTRING (tem)->data]]; + if (cl ==nil) + fatal ("Could not find %s.\n", XSTRING (tem1)->data); + [cl writeToFile: nil]; + } + } + + { + char c[128]; +#ifdef NS_IMPL_GNUSTEP + strncpy (c, gnustep_base_version, sizeof (c)); +#else + /*PSnextrelease (128, c); */ + snprintf (c, sizeof (c), "%g", NSAppKitVersionNumber); +#endif + Vwindow_system_version = build_string (c); + } + + delete_keyboard_wait_descriptor (0); + +/* Set up OS X app menu */ +#ifdef NS_IMPL_COCOA + { + NSMenu *appMenu; + id<NSMenuItem> item; + /* set up the application menu */ + svcsMenu = [[EmacsMenu alloc] initWithTitle: @"Services"]; + [svcsMenu setAutoenablesItems: NO]; + appMenu = [[EmacsMenu alloc] initWithTitle: @"Emacs"]; + [appMenu setAutoenablesItems: NO]; + mainMenu = [[EmacsMenu alloc] initWithTitle: @""]; + + [appMenu insertItemWithTitle: @"About Emacs" + action: @selector (orderFrontStandardAboutPanel:) + keyEquivalent: @"" + atIndex: 0]; + [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 1]; + [appMenu insertItemWithTitle: @"Preferences..." + action: @selector (showPreferencesWindow:) + keyEquivalent: @"," + atIndex: 2]; + [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 3]; + item = [appMenu insertItemWithTitle: @"Services" + action: @selector (menuDown:) + keyEquivalent: @"" + atIndex: 4]; + [appMenu setSubmenu: svcsMenu forItem: item]; +/* [svcsMenu setSupercell: item]; */ + [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 5]; + [appMenu insertItemWithTitle: @"Hide Emacs" + action: @selector (hide:) + keyEquivalent: @"h" + atIndex: 6]; + item = [appMenu insertItemWithTitle: @"Hide Others" + action: @selector (hideOtherApplications:) + keyEquivalent: @"h" + atIndex: 7]; + [item setKeyEquivalentModifierMask: NSCommandKeyMask | NSAlternateKeyMask]; + [appMenu insertItem: [NSMenuItem separatorItem] atIndex: 8]; + [appMenu insertItemWithTitle: @"Quit Emacs" + action: @selector (terminate:) + keyEquivalent: @"q" + atIndex: 9]; + + item = [mainMenu insertItemWithTitle: @"Emacs" + action: @selector (menuDown:) + keyEquivalent: @"" + atIndex: 0]; + [mainMenu setSubmenu: appMenu forItem: item]; + + [NSApp setMainMenu: mainMenu]; + [NSApp setAppleMenu: appMenu]; + [NSApp setServicesMenu: svcsMenu]; + /* Needed at least on Cocoa, to get dock menu to show windows */ + [NSApp setWindowsMenu: [[NSMenu alloc] init]]; + } +#endif /* MAC OS X menu setup */ + + [NSApp run]; + + return dpyinfo; +} + + +extern Lisp_Object Vauto_save_list_file_name; +void +ns_term_shutdown (int sig) +{ + /* code not reached in emacs.c after this is called by shut_down_emacs: */ + if (STRINGP (Vauto_save_list_file_name)) + unlink (XSTRING (Vauto_save_list_file_name)->data); + + ns_shutdown_properly = YES; + [NSApp terminate: NSApp]; +} + + +void +syms_of_nsterm () +{ + NSTRACE (syms_of_nsterm); + DEFVAR_LISP ("ns-input-file", &ns_input_file, + "The file specified in the last NS event."); + ns_input_file =Qnil; + + DEFVAR_LISP ("ns-input-text", &ns_input_text, + "The data received in the last NS text drag event."); + ns_input_text =Qnil; + + DEFVAR_LISP ("ns-working-text", &ns_working_text, + "String for visualizing working composition sequence."); + ns_working_text =Qnil; + + DEFVAR_LISP ("ns-input-font", &ns_input_font, + "The font specified in the last NS event."); + ns_input_font =Qnil; + + DEFVAR_LISP ("ns-input-fontsize", &ns_input_fontsize, + "The fontsize specified in the last NS event."); + ns_input_fontsize =Qnil; + + DEFVAR_LISP ("ns-input-line", &ns_input_line, + "The line specified in the last NS event."); + ns_input_line =Qnil; + + DEFVAR_LISP ("ns-input-color", &ns_input_color, + "The color specified in the last NS event."); + ns_input_color =Qnil; + + DEFVAR_LISP ("ns-input-spi-name", &ns_input_spi_name, + "The service name specified in the last NS event."); + ns_input_spi_name =Qnil; + + DEFVAR_LISP ("ns-input-spi-arg", &ns_input_spi_arg, + "The service argument specified in the last NS event."); + ns_input_spi_arg =Qnil; + + DEFVAR_LISP ("ns-alternate-modifier", &ns_alternate_modifier, + "This variable describes the behavior of the alternate or option key.\n\ +Set to control, meta, alt, super, or hyper means it is taken to be that key.\n\ +Set to none means that the alternate / option key is not interpreted by Emacs\n\ +at all, allowing it to be used at a lower level for accented character entry."); + + DEFVAR_LISP ("ns-command-modifier", &ns_command_modifier, + "This variable describes the behavior of the command key.\n\ +Set to control, meta, alt, super, or hyper means it is taken to be that key."); + + DEFVAR_LISP ("ns-control-modifier", &ns_control_modifier, + "This variable describes the behavior of the control key.\n\ +Set to control, meta, alt, super, or hyper means it is taken to be that key."); + + DEFVAR_LISP ("ns-function-modifier", &ns_function_modifier, + "This variable describes the behavior of the function key (on laptops).\n\ +Set to control, meta, alt, super, or hyper means it is taken to be that key.\n\ +Set to none means that the function key is not interpreted by Emacs at all,\n\ +allowing it to be used at a lower level for accented character entry."); + + DEFVAR_LISP ("ns-cursor-blink-rate", &ns_cursor_blink_rate, + "Rate at which the Emacs cursor blinks (in seconds).\n\ +Set to nil to disable blinking."); + + DEFVAR_LISP ("ns-cursor-blink-mode", &ns_cursor_blink_mode, + "Internal variable -- use M-x blink-cursor-mode or preferences\n\ +panel to control this setting."); + + DEFVAR_LISP ("ns-expand-space", &ns_expand_space, + "Amount by which spacing between lines is expanded (positive)\n\ +or shrunk (negative). Zero (the default) means standard line height.\n\ +(This variable should only be read, never set.)"); + + DEFVAR_BOOL ("ns-antialias-text", &ns_antialias_text, + "Non-nil (the default) means to render text antialiased. Only has an effect on OS X Panther and above."); + + DEFVAR_BOOL ("ns-use-qd-smoothing", &ns_use_qd_smoothing, + "Whether to render text using QuickDraw (less heavy) antialiasing. Only has an effect on OS X Panther and above. Default is nil (use Quartz smoothing)."); + + DEFVAR_BOOL ("ns-use-system-highlight-color", + &ns_use_system_highlight_color, + "Whether to use the system default (on OS X only) for the highlight color. Nil means to use standard emacs (prior to version 21) 'grey'."); + + staticpro (&ns_display_name_list); + ns_display_name_list = Qnil; + + staticpro (&last_mouse_motion_frame); + last_mouse_motion_frame = Qnil; + +/*23: now apparently we need to tell emacs what modifiers there are.. */ + Qmodifier_value = intern ("modifier-value"); + Qalt = intern ("alt"); + Fput (Qalt, Qmodifier_value, make_number (alt_modifier)); + Qhyper = intern ("hyper"); + Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier)); + Qmeta = intern ("meta"); + Fput (Qmeta, Qmodifier_value, make_number (meta_modifier)); + Qsuper = intern ("super"); + Fput (Qsuper, Qmodifier_value, make_number (super_modifier)); + Qcontrol = intern ("control"); + Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier)); + + /*PENDING: move to common code */ + DEFVAR_LISP ("x-toolkit-scroll-bars", &Vx_toolkit_scroll_bars, + doc: /* If not nil, Emacs uses toolkit scroll bars. */); +#ifdef USE_TOOLKIT_SCROLL_BARS + Vx_toolkit_scroll_bars = Qt; +#else + Vx_toolkit_scroll_bars = Qnil; +#endif + + /* these are unsupported but we need the declarations to avoid whining + messages from cus-start.el */ + DEFVAR_BOOL ("x-use-underline-position-properties", + &x_use_underline_position_properties, + doc: /* NOT SUPPORTED UNDER NS. +*Non-nil means make use of UNDERLINE_POSITION font properties. +A value of nil means ignore them. If you encounter fonts with bogus +UNDERLINE_POSITION font properties, for example 7x13 on XFree prior +to 4.1, set this to nil. + +NOTE: Not supported on Mac yet. */); + x_use_underline_position_properties = 0; + + DEFVAR_BOOL ("x-underline-at-descent-line", + &x_underline_at_descent_line, + doc: /* NOT SUPPORTED UNDER NS. +*Non-nil means to draw the underline at the same place as the descent line. +A value of nil means to draw the underline according to the value of the +variable `x-use-underline-position-properties', which is usually at the +baseline level. The default value is nil. */); + x_underline_at_descent_line = 0; + + /* Tell emacs about this window system. */ + Fprovide (intern ("ns-windowing"), Qnil); + /* PENDING: try to move this back into lisp (ns-win.el loaded too late + right now */ + { + Lisp_Object args[3] = { intern ("ns-version-string"), build_string ("9.0"), + build_string ("NS Window system port version number.") }; + Fdefconst (Flist (3, args)); + } +} + + + +/* ========================================================================== + + EmacsApp implementation + + ========================================================================== */ + + +@implementation EmacsApp + +- (void)logNotification: (NSNotification *)notification +{ + const char *name = [[notification name] UTF8String]; + if (!strstr (name, "Update") && !strstr (name, "NSMenu") + && !strstr (name, "WindowNumber")) + NSLog (@"notification: '%@'", [notification name]); +} + + +- (void)sendEvent: (NSEvent *)theEvent +/* -------------------------------------------------------------------------- + Events posted by ns_send_appdefined interrupt the run loop here + -------------------------------------------------------------------------- */ +{ + int type = [theEvent type]; + NSWindow *window = [theEvent window]; +/* NSTRACE (sendEvent); */ +/*fprintf (stderr, "received event of type %d\n", [theEvent type]); */ + + if (type == NSCursorUpdate && window == nil) + { + fprintf (stderr, "Dropping external cursor update event.\n"); + return; + } + +#ifdef NS_IMPL_COCOA + /* pass mouse down in resize handle and subsequent drags directly to + EmacsWindow so we can generate continuous redisplays */ + if (ns_in_resize) + { + if (type == NSLeftMouseDragged) + { + [window mouseDragged: theEvent]; + return; + } + else if (type == NSLeftMouseUp) + { + [window mouseUp: theEvent]; + return; + } + } + else if (type == NSLeftMouseDown) + { + NSRect r = ns_resize_handle_rect (window); + if (NSPointInRect ([theEvent locationInWindow], r)) + { + ns_in_resize = YES; + [window mouseDown: theEvent]; + return; + } + } +#endif + + if (type == NSApplicationDefined) + { + last_appdefined_event = theEvent; + [self stop: self]; + } + + [super sendEvent: theEvent]; +} + + +- (void)showPreferencesWindow: (id)sender +{ + if (prefsController == nil) + prefsController = [[EmacsPrefsController alloc] init]; + [prefsController showForFrame: SELECTED_FRAME ()]; +} + + +/* ************************************************************************** + + EmacsApp delegate implementation + + ************************************************************************** */ + +- (void)applicationDidFinishLaunching: (NSNotification *)notification +/* -------------------------------------------------------------------------- + When application is loaded, terminate event loop in ns_term_init + -------------------------------------------------------------------------- */ +{ + NSTRACE (applicationDidFinishLaunching); + [NSApp setServicesProvider: NSApp]; + ns_send_appdefined (-2); +} + + +- (void) terminate: (id)sender +{ + BLOCK_INPUT; + if (ns_shutdown_properly) + [super terminate: sender]; + else + { +/* Fkill_emacs (Qnil); */ + ns_shutdown_properly = YES; + Feval (Fcons (intern ("save-buffers-kill-emacs"), Qnil)); + } + UNBLOCK_INPUT; +} + + +- (NSApplicationTerminateReply)applicationShouldTerminate: (id)sender +{ + if (ns_shutdown_properly) + return NSTerminateNow; + + Lisp_Object contents = list3 (build_string ("Exit requested. Would you like to Save Buffers and Exit, or Cancel the request?"), + Fcons (build_string ("Cancel"), Qnil), + Fcons (build_string ("Save and Exit"), Qt)); + Lisp_Object res = ns_popup_dialog (Qt, contents, Qnil); +fprintf (stderr, "res = %d\n", res ==Qt); + if (res == Qt) + { + Feval (Fcons (intern ("save-buffers-kill-emacs"), Qnil)); + return NSTerminateNow; + } + return NSTerminateCancel; +} + + +/* Open a file (used by below, after going into queue read by ns_read_socket) */ +-(BOOL) openFile: (NSString *)fileName +{ + struct frame *emacsframe = SELECTED_FRAME (); + NSEvent *theEvent = [NSApp currentEvent]; + + if (!emacs_event) + return NO; + + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_OPEN_FILE_LINE; + ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String])); + ns_input_line = Qnil; /* can be start or cons start,end */ + emacs_event->modifiers =0; + EV_TRAILER (theEvent); + + return YES; +} + + +/* Notification from the Workspace to open a file */ +- (BOOL)application: sender openFile: (NSString *)file +{ + [ns_pending_files addObject: file]; + return YES; +} + + +/* Open a file as a temporary file */ +- (BOOL)application: sender openTempFile: (NSString *)file +{ + [ns_pending_files addObject: file]; + return YES; +} + + +/* Notification from the Workspace to open a file noninteractively (?) */ +- (BOOL)application: sender openFileWithoutUI: (NSString *)file +{ + [ns_pending_files addObject: file]; + return YES; +} + + +/* Notification from the Workspace to open multiple files */ +- (void)application: sender openFiles: (NSArray *)fileList +{ + NSEnumerator *files = [fileList objectEnumerator]; + NSString *file; + while ((file = [files nextObject]) != nil) + [ns_pending_files addObject: file]; + return YES; +} + +/*PENDING: these may help w/IO switching btwn terminal and NSApp */ +- (void)applicationDidBecomeActive: (NSNotification *)notification +{ +} +- (void)applicationDidResignActive: (NSNotification *)notification +{ + ns_send_appdefined (-1); +} + + + +/* ========================================================================== + + EmacsApp aux handlers for managing event loop + + ========================================================================== */ + + +- (void)timeout_handler: (NSTimer *)timedEntry +/* -------------------------------------------------------------------------- + The timeout specified to ns_select has passed. + -------------------------------------------------------------------------- */ +{ + /*NSTRACE (timeout_handler); */ + ns_send_appdefined (-2); +} + +extern void update_window_cursor (struct window *w, int on); + +- (void)cursor_blink_handler: (NSTimer *)cursorEntry +/* -------------------------------------------------------------------------- + Flash the cursor + -------------------------------------------------------------------------- */ +{ + struct ns_display_info *dpyinfo = ns_display_list; /*HACK, but OK for now */ + struct frame *f = dpyinfo->ns_highlight_frame; + NSTRACE (cursor_blink_handler); + + if (!f) + return; + if (f->output_data.ns->current_cursor == no_highlight) + { + Lisp_Object tem = get_frame_param (f, Qcursor_type); + f->output_data.ns->desired_cursor = ns_lisp_to_cursor_type (tem); + } + else + { + f->output_data.ns->desired_cursor = no_highlight; + } + update_window_cursor (XWINDOW (FRAME_SELECTED_WINDOW (f)), 1); + /*x_update_cursor (f, 1); */ +} + + +- (void)fd_handler: (NSTimer *) fdEntry +/* -------------------------------------------------------------------------- + Check data waiting on file descriptors and terminate if so + -------------------------------------------------------------------------- */ +{ + int result; + /* NSTRACE (fd_handler); */ + + if (select_nfds == 0) + return; + + memcpy (&t_readfds, &select_readfds, sizeof (fd_set)); + + select_timeout.tv_sec = select_timeout.tv_usec = 0; + result = select (select_nfds, &t_readfds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, + &select_timeout); + if (result) + { + memcpy (&select_readfds, &t_readfds, sizeof (fd_set)); + ns_send_appdefined (result); + } +} + + + +/* ========================================================================== + + Service provision + + ========================================================================== */ + +/* called from system: queue for next pass through event loop */ +- (void)requestService: (NSPasteboard *)pboard + userData: (NSString *)userData + error: (NSString **)error +{ + [ns_pending_service_names addObject: userData]; + [ns_pending_service_args addObject: [NSString stringWithUTF8String: + SDATA (ns_string_from_pasteboard (pboard))]]; +} + + +/* called from ns_read_socket to clear queue */ +- (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg +{ + struct frame *emacsframe = SELECTED_FRAME (); + NSEvent *theEvent = [NSApp currentEvent]; + + if (!emacs_event) + return NO; + + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_SPI_SERVICE_CALL; + ns_input_spi_name = build_string ([name UTF8String]); + ns_input_spi_arg = build_string ([arg UTF8String]); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + + return YES; +} + + +@end /* EmacsApp */ + + + +/* ========================================================================== + + EmacsView implementation + + ========================================================================== */ + + +@implementation EmacsView + +/* needed to inform when window closed from LISP */ +- (void) setWindowClosing: (BOOL)closing +{ + windowClosing = closing; +} + + +- (void)dealloc +{ + NSTRACE (EmacsView_dealloc); + [toolbar release]; + [super dealloc]; +} + + +/* called on font panel selection */ +- (void)changeFont: (id)sender +{ + NSEvent *e =[[self window] currentEvent]; + struct face *face =FRAME_DEFAULT_FACE (emacsframe); + id newFont; + float size; + + NSTRACE (changeFont); + if (!emacs_event) + return; + + if (newFont = [sender convertFont: + ((struct nsfont_info *)face->font)->nsfont]) + { + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->modifiers = 0; + emacs_event->code = KEY_NS_CHANGE_FONT; + + size = [newFont pointSize]; + /* PENDING: stick w/integer sizes for now. */ +/* if (size == lrint (size)) */ + ns_input_fontsize = make_number (lrint (size)); +/* else + ns_input_fontsize = make_float (size); */ + ns_input_font = build_string ([[newFont familyName] UTF8String]); + EV_TRAILER (e); + } +} + + +- (BOOL)acceptsFirstResponder +{ + NSTRACE (acceptsFirstResponder); + return YES; +} + + +- (void)resetCursorRects +{ + NSRect visible = [self visibleRect]; + NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe); + NSTRACE (resetCursorRects); + + if (currentCursor == nil) + currentCursor = [NSCursor arrowCursor]; + + if (!NSIsEmptyRect (visible)) + [self addCursorRect: visible cursor: currentCursor]; + [currentCursor setOnMouseEntered: YES]; +} + + +/*****************************************************************************/ +/* Keyboard handling. */ +#define NS_KEYLOG 0 + +- (void)keyDown: (NSEvent *)theEvent +{ + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (emacsframe); + int code; + unsigned fnKeysym = 0; + int flags; + static NSMutableArray *nsEvArray; + static BOOL firstTime = YES; + + NSTRACE (keyDown); + + /* Rhapsody and OS X give up and down events for the arrow keys */ + if ([theEvent type] != NSKeyDown) + return; + + if (!emacs_event) + return; + +/*#if defined (COCOA_EXPERIMENTAL_CTRL_G) */ + if (![[self window] isKeyWindow]) + { + /* PENDING: Using NO_SOCK_SIGIO like Carbon causes a condition in which, + when Emacs display updates a different frame from the current one, + and temporarily selects it, then processes some interrupt-driven + input (dispnew.c:3878), OS will send the event to the correct NSWindow, + but for some reason that window has its first responder set to the + NSView most recently updated (I guess), which is not the correct one. + UPDATE: After multi-TTY merge this happens even w/o NO_SOCK_SIGIO */ + if ([[theEvent window] isKindOfClass: [EmacsWindow class]]) + [[(EmacsView *)[theEvent window] delegate] keyDown: theEvent]; + return; + } +/*#endif */ + + if (nsEvArray == nil) + nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; + + [NSCursor setHiddenUntilMouseMoves: YES]; + + if (dpyinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight)) + { + clear_mouse_face (dpyinfo); + dpyinfo->mouse_face_hidden = 1; + } + + if (!processingCompose) + { + code = ([[theEvent charactersIgnoringModifiers] length] == 0) ? + 0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0]; + /* (Carbon way: [theEvent keyCode]) */ + + /* is it a "function key"? */ + fnKeysym = ns_convert_key (code); + if (fnKeysym) + { + /* COUNTERHACK: map 'Delete' on upper-right main KB to 'Backspace', + because Emacs treats Delete and KP-Delete same (in simple.el). */ + if (fnKeysym == 0xFFFF && [theEvent keyCode] == 0x33) + code = 0xFF08; /* backspace */ + else + code = fnKeysym; + } + + /* are there modifiers? */ + emacs_event->modifiers = 0; + flags = [theEvent modifierFlags]; + + if (flags & NSHelpKeyMask) + emacs_event->modifiers |= hyper_modifier; + + if (flags & NSShiftKeyMask) + emacs_event->modifiers |= shift_modifier; + + if (flags & NSCommandKeyMask) + { + emacs_event->modifiers |= lisp_to_mod (ns_command_modifier); + /* if super (default), take input manager's word so things like + dvorak / qwerty layout work */ + if (EQ (ns_command_modifier, Qsuper) + && !fnKeysym + && [[theEvent characters] length] != 0) + { + /* PENDING: the code we get will be unshifted, so if we have + a shift modifier, must convert ourselves */ + if (!(flags & NSShiftKeyMask)) + code = [[theEvent characters] characterAtIndex: 0]; +#if 0 + /* this is ugly and also requires linking w/Carbon framework + (for LMGetKbdType) so for now leave this rare (?) case + undealt with.. in future look into CGEvent methods */ + else + { + long smv = GetScriptManagerVariable (smKeyScript); + Handle uchrHandle = GetResource + ('uchr', GetScriptVariable (smv, smScriptKeys)); + UInt32 dummy = 0; + UCKeyTranslate ((UCKeyboardLayout*)*uchrHandle, + [[theEvent characters] characterAtIndex: 0], + kUCKeyActionDisplay, + (flags & ~NSCommandKeyMask) >> 8, + LMGetKbdType (), kUCKeyTranslateNoDeadKeysMask, + &dummy, 1, &dummy, &code); + code &= 0xFF; + } +#endif + } + } + + if (flags & NSControlKeyMask) + emacs_event->modifiers |= lisp_to_mod (ns_control_modifier); + + if (flags & NS_FUNCTION_KEY_MASK && !fnKeysym) + emacs_event->modifiers |= lisp_to_mod (ns_function_modifier); + + if (flags & NSAlternateKeyMask) /* default = meta */ + { + if (EQ (ns_alternate_modifier, Qnone) && !fnKeysym) + { /* accept pre-interp alt comb */ + if ([[theEvent characters] length] > 0) + code = [[theEvent characters] characterAtIndex: 0]; + /*HACK: clear lone shift modifier to stop next if from firing */ + if (emacs_event->modifiers == shift_modifier) + emacs_event->modifiers = 0; + } + else + emacs_event->modifiers |= lisp_to_mod (ns_alternate_modifier); + } + +/*fprintf (stderr,"code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",code,fnKeysym,flags,emacs_event->modifiers); */ + + /* if it was a function key or had modifiers, pass it directly to emacs */ + if (fnKeysym || (emacs_event->modifiers + && [[theEvent charactersIgnoringModifiers] length] > 0)) +/*[[theEvent characters] length] */ + { + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + if (code < 0x20) + code |= (1<<28)|(3<<16); + else if (code == 0x7f) + code |= (1<<28)|(3<<16); + else if (!fnKeysym) + emacs_event->kind = code > 0xFF + ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT; + + emacs_event->code = code; + EV_TRAILER (theEvent); + return; + } + } + + /* if we get here we should send the key for input manager processing */ + if (firstTime && [[NSInputManager currentInputManager] + wantsToDelayTextChangeNotifications] == NO) + fprintf (stderr, + "Emacs: WARNING: TextInput mgr wants marked text to be permanent!\n"); + firstTime = NO; + + if (NS_KEYLOG && !processingCompose) + fprintf (stderr, "Begin compose sequence.\n"); + + processingCompose = YES; + [nsEvArray addObject: theEvent]; + [self interpretKeyEvents: nsEvArray]; + [nsEvArray removeObject: theEvent]; +} + + +/* <NSTextInput> implementation (called through super interpretKeyEvents:]). */ + + +/* <NSTextInput>: called through when done composing */ +- (void)insertText: (id)aString +{ + int code; + int len = [(NSString *)aString length]; + int i; + +if (NS_KEYLOG) NSLog (@"insertText '%@'\tlen = %d", aString, len); + processingCompose = NO; + + if (!emacs_event) + return; + + /* first, clear any working text */ + if (workingText != nil) + [self deleteWorkingText]; + + /* now insert the string as keystrokes */ + for (i =0; i<len; i++) + { + code = [aString characterAtIndex: i]; + /* PENDING: still need this? */ + if (code == 0x2DC) + code = '~'; /* 0x7E */ + emacs_event->modifiers = 0; + emacs_event->kind = + code > 0xFF ? MULTIBYTE_CHAR_KEYSTROKE_EVENT : ASCII_KEYSTROKE_EVENT; + emacs_event->code = code; + EV_TRAILER ((id)nil); + } +} + + +/* <NSTextInput>: inserts display of composing characters */ +- (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange +{ + NSString *str = [aString respondsToSelector: @selector (string)] ? + [aString string] : aString; + if (NS_KEYLOG) + NSLog (@"setMarkedText '%@' len =%d range %d from %d", str, [str length], + selRange.length, selRange.location); + + if (workingText != nil) + [self deleteWorkingText]; + if ([str length] == 0) + return; + + if (!emacs_event) + return; + + processingCompose = YES; + workingText = [str copy]; + ns_working_text = build_string ([workingText UTF8String]); + + /* if in "echo area", not true minibuffer, can't show chars in interactive + mode, so call using eval; otherwise we send a key event, which was the + original way this was done */ + if (!EQ (Feval (Fcons (intern ("ns-in-echo-area"), Qnil)), Qnil)) + { + Feval (Fcons (intern ("ns-echo-working-text"), Qnil)); + ns_send_appdefined (-1); + } + else + { + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_INSERT_WORKING_TEXT; + EV_TRAILER ((id)nil); + } +} + + +/* delete display of composing characters [not in <NSTextInput>] */ +- (void)deleteWorkingText +{ + if (workingText == nil) + return; + if (NS_KEYLOG) + fprintf (stderr, "deleteWorkingText len =%d\n", [workingText length]); + [workingText release]; + workingText = nil; + processingCompose = NO; + + if (!emacs_event) + return; + + if (!EQ (Feval (Fcons (intern ("ns-in-echo-area"), Qnil)), Qnil)) + { + Feval (Fcons (intern ("ns-unecho-working-text"), Qnil)); + ns_send_appdefined (-1); + } + else + { + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_DELETE_WORKING_TEXT; + EV_TRAILER ((id)nil); + } + } + + +- (BOOL)hasMarkedText +{ + return workingText != nil; +} + +- (NSRange)markedRange +{ + NSRange rng = workingText != nil + ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0); +if (NS_KEYLOG) NSLog (@"markedRange request"); + return rng; +} + +- (void)unmarkText +{ +if (NS_KEYLOG) NSLog (@"unmark (accept) text"); + [self deleteWorkingText]; + processingCompose = NO; +} + +/* used to position char selection windows, etc. */ +- (NSRect)firstRectForCharacterRange: (NSRange)theRange +{ + NSRect rect; + NSPoint pt; + struct window *win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)); +if (NS_KEYLOG) NSLog (@"firstRectForCharRange request"); + + rect.size.width = theRange.length * FRAME_COLUMN_WIDTH (emacsframe); + rect.size.height = FRAME_LINE_HEIGHT (emacsframe); + pt.x = WINDOW_TEXT_TO_FRAME_PIXEL_X (win, win->phys_cursor.x); + pt.y = WINDOW_TO_FRAME_PIXEL_Y (win, win->phys_cursor.y + +FRAME_LINE_HEIGHT (emacsframe)); + + pt = [self convertPoint: pt toView: nil]; + pt = [[self window] convertBaseToScreen: pt]; + rect.origin = pt; + return rect; +} + +- (long)conversationIdentifier +{ + return (long)self; +} + +/*PENDING: below here not yet implemented correctly, but may not be needed */ + +- (void)doCommandBySelector: (SEL)aSelector +{ + if (NS_KEYLOG) NSLog (@"Do command by selector: %@", + NSStringFromSelector (aSelector)); + + if (aSelector == @selector (deleteBackward:)) + { + /* happens when user backspaces over an ongoing composition: + throw a 'delete' into the event queue */ + if (!emacs_event) + return; + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = 0xFF08; + EV_TRAILER ((id)nil); + } +} + +- (NSArray *)validAttributesForMarkedText +{ + static NSArray *arr = nil; + if (arr == nil) arr = [NSArray new]; + /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */ + return arr; +} + +- (NSRange)selectedRange +{ +if (NS_KEYLOG) NSLog (@"selectedRange request"); + return NSMakeRange (NSNotFound, 0); +} + +- (unsigned int)characterIndexForPoint: (NSPoint)thePoint +{ +if (NS_KEYLOG) NSLog (@"characterIndexForPoint request"); + return 0; +} + +- (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange +{ + static NSAttributedString *str = nil; + if (str == nil) str = [NSAttributedString new]; +if (NS_KEYLOG) NSLog (@"attributedSubstringFromRange request"); + return str; +} + +/* End <NSTextInput> impl. */ +/*****************************************************************************/ + + +/* This is what happens when the user presses a mouse button. */ +- (void)mouseDown: (NSEvent *)theEvent +{ + NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil]; + Lisp_Object window; + + NSTRACE (mouseDown); + + [self deleteWorkingText]; + + if (!emacs_event) + return; + + last_mouse_frame = emacsframe; + /* appears to be needed to prevent spurious movement events generated on + button clicks */ + last_mouse_frame->mouse_moved = 0; + + if ([theEvent type] == NSScrollWheel) + { + float delta = [theEvent deltaY]; + /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */ + if (delta == 0) + return; + emacs_event->kind = WHEEL_EVENT; + emacs_event->code = 0; + emacs_event->modifiers = EV_MODIFIERS (theEvent) | + ((delta > 0) ? up_modifier : down_modifier); + } + else + { + emacs_event->kind = MOUSE_CLICK_EVENT; + emacs_event->code = EV_BUTTON (theEvent); + emacs_event->modifiers = EV_MODIFIERS (theEvent) + | EV_UDMODIFIERS (theEvent); + } + XSETINT (emacs_event->x, lrint (p.x)); + XSETINT (emacs_event->y, lrint (p.y)); + EV_TRAILER (theEvent); +} + + +- (void)mouseUp: (NSEvent *)theEvent +{ + NSTRACE (mouseUp); + [self mouseDown: theEvent]; +} + + +- (void)rightMouseDown: (NSEvent *)theEvent +{ + NSTRACE (rightMouseDown); + [self mouseDown: theEvent]; +} + + +- (void)rightMouseUp: (NSEvent *)theEvent +{ + NSTRACE (rightMouseUp); + [self mouseDown: theEvent]; +} + + +- (void) scrollWheel: (NSEvent *)theEvent +{ + NSTRACE (scrollWheel); + [self mouseDown: theEvent]; +} + + +/* Tell emacs the mouse has moved. */ +- (void)mouseMoved: (NSEvent *)e +{ + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (emacsframe); + Lisp_Object frame; + + NSTRACE (mouseMoved); + + last_mouse_movement_time = EV_TIMESTAMP (e); + last_mouse_motion_position = + [self convertPoint: [e locationInWindow] fromView: nil]; + + /* update any mouse face */ + if (dpyinfo->mouse_face_hidden) + { + dpyinfo->mouse_face_hidden = 0; + clear_mouse_face (dpyinfo); + } + + /* tooltip handling */ + previous_help_echo_string = help_echo_string; + help_echo_string = Qnil; + + if (!note_mouse_movement (emacsframe, last_mouse_motion_position.x, + last_mouse_motion_position.y)) + help_echo_string = previous_help_echo_string; + + XSETFRAME (frame, emacsframe); + if (!NILP (help_echo_string) || !NILP (previous_help_echo_string)) + { + /* NOTE: help_echo_{window,pos,object} are set in xdisp.c + (note_mouse_highlight), which is called through the + note_mouse_movement () call above */ + gen_help_event (help_echo_string, frame, help_echo_window, + help_echo_object, help_echo_pos); + } + else + { + help_echo_string = Qnil; + gen_help_event (Qnil, frame, Qnil, Qnil, 0); + } + + if (emacsframe->mouse_moved && send_appdefined) + ns_send_appdefined (-1); +} + + +- (void)mouseDragged: (NSEvent *)e +{ + NSTRACE (mouseDragged); + [self mouseMoved: e]; +} + + +- (void)rightMouseDragged: (NSEvent *)e +{ + NSTRACE (rightMouseDragged); + [self mouseMoved: e]; +} + + +- (BOOL)windowShouldClose: (id)sender +{ + NSEvent *e =[[self window] currentEvent]; + + NSTRACE (windowShouldClose); + windowClosing = YES; + if (ns_window_num <= 1) + return NO; + if (!emacs_event) + return NO; + emacs_event->kind = DELETE_WINDOW_EVENT; + emacs_event->modifiers = 0; + emacs_event->code = 0; + EV_TRAILER (e); + /* Don't close this window, let this be done from lisp code. */ + return NO; +} + + +- (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize +/* normalize frame to gridded text size */ +{ + NSTRACE (windowWillResize); +/*fprintf (stderr,"Window will resize: %.0f x %.0f\n",frameSize.width,frameSize.height); */ + + cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, +#ifdef NS_IMPL_GNUSTEP + frameSize.width + 3); +#else + frameSize.width); +#endif + if (cols < MINWIDTH) + cols = MINWIDTH; + frameSize.width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (emacsframe, cols); + + rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, frameSize.height +#ifdef NS_IMPL_GNUSTEP + - FRAME_NS_TITLEBAR_HEIGHT (emacsframe) + 3 + - FRAME_NS_TOOLBAR_HEIGHT (emacsframe)); +#else + - FRAME_NS_TITLEBAR_HEIGHT (emacsframe) + - FRAME_NS_TOOLBAR_HEIGHT (emacsframe)); +#endif + if (rows < MINHEIGHT) + rows = MINHEIGHT; + frameSize.height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (emacsframe, rows) + + FRAME_NS_TITLEBAR_HEIGHT (emacsframe) + + FRAME_NS_TOOLBAR_HEIGHT (emacsframe); +#ifdef NS_IMPL_COCOA + { + /* this sets window title to have size in it; the wm does this under GS */ + NSRect r = [[self window] frame]; + if (r.size.height == frameSize.height && r.size.width == frameSize.width) + { + if (old_title != 0) + { + xfree (old_title); + old_title = 0; + } + } + else + { + char *size_title; + NSWindow *window = [self window]; + if (old_title == 0) + { + const char *t = [[[self window] title] UTF8String]; + char *pos = strstr (t, " — "); + if (pos) + *pos = '\0'; + old_title = (char *) xmalloc (strlen (t) + 1); + strcpy (old_title, t); + } + size_title = xmalloc (strlen (old_title) + 40); + sprintf (size_title, "%s — (%d x %d)", old_title, cols, rows); + [window setTitle: [NSString stringWithUTF8String: size_title]]; + [window display]; + xfree (size_title); + } + } +#endif /* NS_IMPL_COCOA */ +/*fprintf (stderr," ...size became %.0f x %.0f (%d x %d)\n",frameSize.width,frameSize.height,cols,rows); */ + + return frameSize; +} + + +- (void)windowDidResize: (NSNotification *)notification +{ + NSWindow *theWindow = [notification object]; + +#ifdef NS_IMPL_GNUSTEP + /* in GNUstep, at least currently, it's possible to get a didResize + without getting a willResize.. therefore we need to act as if we got + the willResize now */ + NSSize sz = [theWindow frame].size; + sz = [self windowWillResize: theWindow toSize: sz]; +#endif /* NS_IMPL_GNUSTEP */ + + NSTRACE (windowDidResize); +/*fprintf (stderr,"windowDidResize: %.0f\n",[theWindow frame].size.height); */ + +#ifdef NS_IMPL_COCOA + if (old_title != 0) + { + xfree (old_title); + old_title = 0; + } +#endif /* NS_IMPL_COCOA */ + + if (cols > 0 && rows > 0) + x_set_window_size (emacsframe, 0, cols, rows); + + ns_send_appdefined (-1); + [NSApp stopModal]; +} + + +- (void)windowDidBecomeKey: (NSNotification *)notification +{ + int val = ns_lisp_to_cursor_type (get_frame_param (emacsframe, Qcursor_type)); + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (emacsframe); + struct frame *old_focus = dpyinfo->ns_focus_frame; + + NSTRACE (windowDidBecomeKey); + + if (emacsframe != old_focus) + dpyinfo->ns_focus_frame = emacsframe; + /*/last_mouse_frame = emacsframe;? */ + + if (val >= 0) + { + FRAME_NEW_CURSOR (emacsframe) = val; +/* x_update_cursor (emacsframe, 1); // will happen in ns_frame_rehighlight */ + } + + ns_frame_rehighlight (emacsframe); + + if (emacs_event) + { + emacs_event->kind = FOCUS_IN_EVENT; + EV_TRAILER ((id)nil); + } +} + + +- (void)windowDidResignKey: (NSNotification *)notification +{ + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (emacsframe); + NSTRACE (windowDidResignKey); + + if (!windowClosing && [[self window] isVisible] == YES) + { + FRAME_NEW_CURSOR (emacsframe) = hollow_box; + x_update_cursor (emacsframe, 1); + FRAME_LAST_INACTIVE (emacsframe) = YES; + } + + if (dpyinfo->ns_highlight_frame == emacsframe) + dpyinfo->ns_highlight_frame = 0; + if (dpyinfo->ns_focus_frame == emacsframe) + dpyinfo->ns_focus_frame = 0; + + if (dpyinfo->mouse_face_mouse_frame == emacsframe) + { + clear_mouse_face (dpyinfo); + dpyinfo->mouse_face_mouse_frame = 0; + } + + if (emacs_event) + { + [self deleteWorkingText]; + emacs_event->kind = FOCUS_IN_EVENT; + EV_TRAILER ((id)nil); + } +} + + +- (void)windowWillMiniaturize: sender +{ + NSTRACE (windowWillMiniaturize); +} + + +- (BOOL)isFlipped +{ + return YES; +} + + +- (BOOL)isOpaque +{ + return NO; +} + + +- initFrameFromEmacs: (struct frame *)f +{ + NSRect r, wr; + Lisp_Object tem; + NSWindow *win; + NSButton *toggleButton; + int vbextra = NS_SCROLL_BAR_WIDTH (f); + NSSize sz; + NSColor *col; + NSString *name; + + NSTRACE (initFrameFromEmacs); + + windowClosing = NO; + processingCompose = NO; + scrollbarsNeedingUpdate = 0; + +/*fprintf (stderr,"init with %d, %d\n",f->text_cols, f->text_lines); */ + + r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols), + FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines)); + [self initWithFrame: r]; + + FRAME_NS_VIEW (f) = self; + emacsframe = f; + old_title = 0; + + win = [[EmacsWindow alloc] + initWithContentRect: r + styleMask: (NSResizableWindowMask | + NSMiniaturizableWindowMask | + NSClosableWindowMask) + backing: NSBackingStoreBuffered + defer: YES]; + + wr = [win frame]; + f->border_width = wr.size.width - r.size.width; + FRAME_NS_TITLEBAR_HEIGHT (f) = wr.size.height - r.size.height; + + [win setAcceptsMouseMovedEvents: YES]; + [win setDelegate: self]; + [win useOptimizedDrawing: YES]; + + sz.width = FRAME_COLUMN_WIDTH (f); + sz.height = FRAME_LINE_HEIGHT (f); + [win setResizeIncrements: sz]; + + [[win contentView] addSubview: self]; + + if (ns_drag_types) + [self registerForDraggedTypes: ns_drag_types]; + + tem = f->name; + name = [NSString stringWithUTF8String: + NILP (tem) ? (unsigned char *)"Emacs" : XSTRING (tem)->data]; + [win setTitle: name]; + + /* toolbar support */ + toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier: + [NSString stringWithFormat: @"Emacs Frame %d", + ns_window_num]]; + [win setToolbar: toolbar]; + [toolbar setVisible: NO]; +#ifdef NS_IMPL_COCOA + toggleButton = [win standardWindowButton: NSWindowToolbarButton]; + [toggleButton setTarget: self]; + [toggleButton setAction: @selector (toggleToolbar: )]; +#endif + FRAME_NS_TOOLBAR_HEIGHT (f) = 0; + + tem = f->icon_name; + if (!NILP (tem)) + [win setMiniwindowTitle: + [NSString stringWithUTF8String: XSTRING (tem)->data]]; + + { + NSScreen *screen = [win screen]; + + if (screen != 0) + [win setFrameTopLeftPoint: NSMakePoint + (IN_BOUND (-SCREENMAX, f->left_pos, SCREENMAX), + IN_BOUND (-SCREENMAX, + [screen frame].size.height - NS_TOP_POS (f), SCREENMAX))]; + } + + [win makeFirstResponder: self]; + + col = ns_lookup_indexed_color (NS_FACE_BACKGROUND + (FRAME_DEFAULT_FACE (emacsframe)), emacsframe); + [win setBackgroundColor: col]; + if ([col alphaComponent] != 1.0) + [win setOpaque: NO]; + + [self allocateGState]; + + ns_window_num++; + return self; +} + + +- (void)windowDidMove: sender +{ + NSWindow *win = [self window]; + NSRect r = [win frame]; + NSScreen *screen = [win screen]; + NSRect sr = [screen frame]; + + NSTRACE (windowDidMove); + + if (!emacsframe->output_data.ns) + return; + if (screen != nil) + { + emacsframe->left_pos = r.origin.x; /* - sr.origin.x; */ + emacsframe->top_pos = sr.size.height - + (r.origin.y + r.size.height); /* + sr.origin.y; */ + } +} + +#ifdef NS_IMPL_COCOA +/* if we don't do this manually, the window will resize but not move */ +- (BOOL)windowShouldZoom: (NSWindow *)sender toFrame: (NSRect)newFrame +{ + [[self window] setFrame: newFrame display: NO]; + return YES; +} +#endif + +/* Implement this to control size of frame on zoom. +- (NSRect)windowWillUseStandardFrame:(NSWindow *)sender + defaultFrame:(NSRect)defaultFrame; */ + + +- (void)windowDidDeminiaturize: sender +{ + NSTRACE (windowDidDeminiaturize); + if (!emacsframe->output_data.ns) + return; + emacsframe->async_visible = 1; + emacsframe->async_iconified = 0; + windows_or_buffers_changed++; + + if (emacs_event) + { + emacs_event->kind = ICONIFY_EVENT; + EV_TRAILER ((id)nil); + } +} + + +- (void)windowDidExpose: sender +{ + NSTRACE (windowDidExpose); + if (!emacsframe->output_data.ns) + return; + emacsframe->async_visible = 1; + SET_FRAME_GARBAGED (emacsframe); + + if (send_appdefined) + ns_send_appdefined (-1); +} + + +- (void)windowDidMiniaturize: sender +{ + NSTRACE (windowDidMiniaturize); + if (!emacsframe->output_data.ns) + return; + + emacsframe->async_iconified = 1; + + if (emacs_event) + { + emacs_event->kind = ICONIFY_EVENT; + EV_TRAILER ((id)nil); + } +} + + +- (void)mouseEntered: (NSEvent *)theEvent +{ + NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil]; + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (emacsframe); + NSTRACE (mouseEntered); + + last_mouse_movement_time = EV_TIMESTAMP (theEvent); +} + + +- (void)mouseExited: (NSEvent *)theEvent +{ + NSPoint p = [self convertPoint: [theEvent locationInWindow] fromView: nil]; + NSRect r; + struct ns_display_info *dpyinfo = + emacsframe ? FRAME_NS_DISPLAY_INFO (emacsframe) : NULL; + + NSTRACE (mouseExited); + + if (dpyinfo || !emacsframe) + return; + + last_mouse_movement_time = EV_TIMESTAMP (theEvent); + + if (emacsframe == dpyinfo->mouse_face_mouse_frame) + { + clear_mouse_face (dpyinfo); + dpyinfo->mouse_face_mouse_frame = 0; + } +} + + +- menuDown: sender +{ + NSTRACE (menuDown); + if (context_menu_value == -1) + context_menu_value = [sender tag]; + else + find_and_call_menu_selection (emacsframe, emacsframe->menu_bar_items_used, + emacsframe->menu_bar_vector, [sender tag]); + ns_send_appdefined (-1); + return self; +} + + +- (EmacsToolbar *)toolbar +{ + return toolbar; +} + + +/* this gets called on toolbar button click */ +- toolbarClicked: (id)item +{ + NSEvent *theEvent; + int idx = [item tag] * TOOL_BAR_ITEM_NSLOTS; + + NSTRACE (toolbarClicked); + + if (!emacs_event) + return self; + + /* send first event (for some reason two needed) */ + theEvent =[[self window] currentEvent]; + emacs_event->kind = TOOL_BAR_EVENT; + XSETFRAME (emacs_event->arg, emacsframe); + EV_TRAILER (theEvent); + + emacs_event->kind = TOOL_BAR_EVENT; +/* XSETINT (emacs_event->code, 0); */ + emacs_event->arg = AREF (emacsframe->tool_bar_items, + idx + TOOL_BAR_ITEM_KEY); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + return self; +} + + +- toggleToolbar: (id)sender +{ + Lisp_Object lispFrame; + XSETFRAME (lispFrame, emacsframe); + Feval (Fcons (intern ("ns-toggle-toolbar"), Fcons (lispFrame, Qnil))); + SET_FRAME_GARBAGED (emacsframe); + ns_send_appdefined (-1); +} + + +- (void)drawRect: (NSRect)rect +{ + int x = NSMinX (rect), y = NSMinY (rect); + int width = NSWidth (rect), height = NSHeight (rect); + + NSTRACE (drawRect); + + if (!emacsframe || !emacsframe->output_data.ns) + return; + + if (!ns_in_resize) + ns_clear_frame_area (emacsframe, x, y, width, height); + expose_frame (emacsframe, x, y, width, height); + + emacsframe->async_visible = 1; + emacsframe->async_iconified = 0; + +/* SET_FRAME_GARBAGED (emacsframe); + ns_send_appdefined (-1); */ +} + + +/* NSDraggingDestination protocol methods. Actually this is not really a + protocol, but a category of Object. O well... */ + +-(unsigned int) draggingEntered: (id <NSDraggingInfo>) sender +{ + NSTRACE (draggingEntered); + return NSDragOperationGeneric; +} + + +-(BOOL)prepareForDragOperation: (id <NSDraggingInfo>) sender +{ + return YES; +} + + +-(BOOL)performDragOperation: (id <NSDraggingInfo>) sender +{ + id pb; + int x, y; + NSString *type; + NSEvent *theEvent = [[self window] currentEvent]; + NSPoint position; + + NSTRACE (performDragOperation); + + if (!emacs_event) + return; + + position = [self convertPoint: [sender draggingLocation] fromView: nil]; + x = lrint (position.x); y = lrint (position.y); + + pb = [sender draggingPasteboard]; + type = [pb availableTypeFromArray: ns_drag_types]; + if (type == 0) + { + return NO; + } + else if ([type isEqualToString: NSFilenamesPboardType]) + { + NSArray *files; + NSEnumerator *fenum; + NSString *file; + + if (!(files = [pb propertyListForType: type])) + return NO; + + fenum = [files objectEnumerator]; + while ( (file = [fenum nextObject]) ) + { + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_DRAG_FILE; + XSETINT (emacs_event->x, x); + XSETINT (emacs_event->y, y); + ns_input_file = append2 (ns_input_file, + build_string ([file UTF8String])); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + } + return YES; + } + else if ([type isEqualToString: NSURLPboardType]) + { + NSString *file; + NSURL *fileURL; + + if (!(fileURL = [NSURL URLFromPasteboard: pb]) || + [fileURL isFileURL] == NO) + return NO; + + file = [fileURL path]; + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_DRAG_FILE; + XSETINT (emacs_event->x, x); + XSETINT (emacs_event->y, y); + ns_input_file = append2 (ns_input_file, build_string ([file UTF8String])); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + return YES; + } + else if ([type isEqualToString: NSStringPboardType] + || [type isEqualToString: NSTabularTextPboardType]) + { + NSString *data; + + if (! (data = [pb stringForType: type])) + return NO; + + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_DRAG_TEXT; + XSETINT (emacs_event->x, x); + XSETINT (emacs_event->y, y); + ns_input_text = build_string ([data UTF8String]); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + return YES; + } + else if ([type isEqualToString: NSColorPboardType]) + { + NSColor *c = [NSColor colorFromPasteboard: pb]; + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_DRAG_COLOR; + XSETINT (emacs_event->x, x); + XSETINT (emacs_event->y, y); + ns_input_color = ns_color_to_lisp (c); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + return YES; + } + else if ([type isEqualToString: NSFontPboardType]) + { + /* impl based on GNUstep NSTextView.m */ + NSData *data = [pb dataForType: NSFontPboardType]; + NSDictionary *dict = [NSUnarchiver unarchiveObjectWithData: data]; + NSFont *font = [dict objectForKey: NSFontAttributeName]; + char fontSize[10]; + + if (font == nil) + return NO; + + emacs_event->kind = NON_ASCII_KEYSTROKE_EVENT; + emacs_event->code = KEY_NS_CHANGE_FONT; + XSETINT (emacs_event->x, x); + XSETINT (emacs_event->y, y); + ns_input_font = build_string ([[font fontName] UTF8String]); + snprintf (fontSize, 10, "%f", [font pointSize]); + ns_input_fontsize = build_string (fontSize); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + return YES; + } + else + { + error ("Invalid data type in dragging pasteboard."); + return NO; + } +} + + +- validRequestorForSendType: (NSString *)typeSent + returnType: (NSString *)typeReturned +{ + NSTRACE (validRequestorForSendType); + if ([ns_send_types indexOfObjectIdenticalTo: typeSent] != NSNotFound && + [ns_return_types indexOfObjectIdenticalTo: typeSent] != NSNotFound) + return self; + + return [super validRequestorForSendType: typeSent + returnType: typeReturned]; +} + + +/* setMini =YES means set from internal (gives a finder icon), NO means set nil + (gives a miniaturized version of the window); currently we use the latter for + frames whose active buffer doesn't correspond to any file + (e.g., '*scratch*') */ +- setMiniwindowImage: (BOOL) setMini +{ + id image = [[self window] miniwindowImage]; + NSTRACE (setMiniwindowImage); + + /* NOTE: under Cocoa miniwindowImage always returns nil, documentation + about "AppleDockIconEnabled" notwithstanding, however the set message + below has its effect nonetheless. */ + if (image != emacsframe->output_data.ns->miniimage) + { + if (image && [image isKindOfClass: [EmacsImage class]]) + [image release]; + [[self window] setMiniwindowImage: + setMini ? emacsframe->output_data.ns->miniimage : nil]; + } + + return self; +} + + +- (void) setRows: (int) r andColumns: (int) c +{ + rows = r; + cols = c; +} + +@end /* EmacsView */ + + + +/* ========================================================================== + + EmacsWindow implementation + + ========================================================================== */ + +@implementation EmacsWindow + +/* called only on resize clicks by special case in EmacsApp-sendEvent */ +- (void)mouseDown: (NSEvent *)theEvent +{ + if (ns_in_resize) + { + NSSize size = [[theEvent window] frame].size; + grabOffset = [theEvent locationInWindow]; + grabOffset.x = size.width - grabOffset.x; + } + else + [super mouseDown: theEvent]; +} + + +/* stop resizing */ +- (void)mouseUp: (NSEvent *)theEvent +{ + if (ns_in_resize) + { + struct frame *f = ((EmacsView *)[self delegate])->emacsframe; + ns_in_resize = NO; + ns_set_name_as_filename (f); + [self display]; + ns_send_appdefined (-1); + } + else + [super mouseUp: theEvent]; +} + + +/* send resize events */ +- (void)mouseDragged: (NSEvent *)theEvent +{ + if (ns_in_resize) + { + NSPoint p = [theEvent locationInWindow]; + NSSize size, vettedSize, origSize = [self frame].size; + + size.width = p.x + grabOffset.x; + size.height = origSize.height - p.y + grabOffset.y; + + if (size.width == origSize.width && size.height == origSize.height) + return; + + vettedSize = [[self delegate] windowWillResize: self toSize: size]; + if (vettedSize.width != size.width || vettedSize.height != size.height) + { + [[NSNotificationCenter defaultCenter] + postNotificationName: NSWindowDidResizeNotification + object: self]; + } + } + else + [super mouseDragged: theEvent]; +} + +@end /* EmacsWindow */ + + +/* ========================================================================== + + EmacsScroller implementation + + ========================================================================== */ + + +@implementation EmacsScroller + +/* for repeat button push */ +#define SCROLL_BAR_FIRST_DELAY 0.5 +#define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15) + ++ (float) scrollerWidth +{ + /* PENDING: if we want to allow variable widths, this is the place to do it, + however neither GNUstep nor Cocoa support it very well */ + return [NSScroller scrollerWidth]; +} + + +- initFrame: (NSRect )r window: (Lisp_Object)nwin +{ + NSTRACE (EmacsScroller_initFrame); + + r.size.width = [EmacsScroller scrollerWidth]; + [super initWithFrame: r/*NSMakeRect (0, 0, 0, 0)*/]; + [self setContinuous: YES]; + [self setEnabled: YES]; + + /* Ensure auto resizing of scrollbars occurs within the emacs frame's view + locked against the right, top and bottom edges. */ + [self setAutoresizingMask: NSViewMinXMargin | NSViewHeightSizable]; + + win = nwin; + condemned = NO; + pixel_height = NSHeight (r); + min_portion = 20 / pixel_height; + + frame = XFRAME (XWINDOW (win)->frame); + if (FRAME_LIVE_P (frame)) + { + int i; + EmacsView *view = FRAME_NS_VIEW (frame); + NSView *sview = [[view window] contentView]; + NSArray *subs = [sview subviews]; + + /* disable optimization stopping redraw of other scrollbars */ + view->scrollbarsNeedingUpdate = 0; + for (i =[subs count]-1; i >= 0; i--) + if ([[subs objectAtIndex: i] isKindOfClass: [EmacsScroller class]]) + view->scrollbarsNeedingUpdate++; + [sview addSubview: self]; + } + +/* [self setFrame: r]; */ + + return self; +} + + +- (void)setFrame: (NSRect)newRect +{ + NSTRACE (EmacsScroller_setFrame); +/* BLOCK_INPUT; */ + pixel_height = NSHeight (newRect); + min_portion = 20 / pixel_height; + [super setFrame: newRect]; + [self display]; +/* UNBLOCK_INPUT; */ +} + + +- (void)dealloc +{ + NSTRACE (EmacsScroller_dealloc); + if (!NILP (win)) + XWINDOW (win)->vertical_scroll_bar = Qnil; + [super dealloc]; +} + + +- condemn +{ + NSTRACE (condemn); + condemned =YES; + return self; +} + + +- reprieve +{ + NSTRACE (reprieve); + condemned =NO; + return self; +} + + +- judge +{ + NSTRACE (judge); + if (condemned) + { + BLOCK_INPUT; + /* ensure other scrollbar updates after deletion */ + EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame); + if (view != nil) + view->scrollbarsNeedingUpdate++; + [self removeFromSuperview]; + [self release]; + UNBLOCK_INPUT; + } + return self; +} + + +- (void)resetCursorRects +{ + NSRect visible = [self visibleRect]; + NSTRACE (resetCursorRects); + + if (!NSIsEmptyRect (visible)) + [self addCursorRect: visible cursor: [NSCursor arrowCursor]]; + [[NSCursor arrowCursor] setOnMouseEntered: YES]; +} + + +- (int) checkSamePosition: (int) position portion: (int) portion + whole: (int) whole +{ + return em_position ==position && em_portion ==portion && em_whole ==whole + && portion != whole; /* needed for resize empty buf */ +} + + +- setPosition: (int)position portion: (int)portion whole: (int)whole +{ + NSTRACE (setPosition); + + em_position = position; + em_portion = portion; + em_whole = whole; + + if (portion >= whole) + [self setFloatValue: 0.0 knobProportion: 1.0]; + else + { + float pos, por; + portion = max ((float)whole*min_portion/pixel_height, portion); + pos = (float)position / (whole - portion); + por = (float)portion/whole; + [self setFloatValue: pos knobProportion: por]; + } +#ifdef NS_IMPL_GNUSTEP + [self display]; +#endif + return self; +} + +/* PENDING: unused at moment (see ns_mouse_position) at the moment because + drag events will go directly to the EmacsScroller. Leaving in for now. */ +-(void)getMouseMotionPart: (int *)part window: (Lisp_Object *)window + x: (Lisp_Object *)x y: ( Lisp_Object *)y +{ + *part = last_hit_part; + *window = win; + XSETINT (*y, pixel_height); + if ([self floatValue] > 0.999) + XSETINT (*x, pixel_height); + else + XSETINT (*x, pixel_height * [self floatValue]); +} + + +/* set up emacs_event */ +- (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e +{ + if (!emacs_event) + return; + + emacs_event->part = last_hit_part; + emacs_event->code = 0; + emacs_event->modifiers = EV_MODIFIERS (e) | down_modifier; + emacs_event->frame_or_window = win; + emacs_event->timestamp = EV_TIMESTAMP (e); + emacs_event->kind = SCROLL_BAR_CLICK_EVENT; + emacs_event->arg = Qnil; + XSETINT (emacs_event->x, loc * pixel_height); + XSETINT (emacs_event->y, pixel_height-20); + + n_emacs_events_pending++; + kbd_buffer_store_event_hold (emacs_event, q_event_ptr); + EVENT_INIT (*emacs_event); + ns_send_appdefined (-1); +} + + +/* called manually thru timer to implement repeated button action w/hold-down */ +- repeatScroll: (NSTimer *)scrollEntry +{ + NSEvent *e = [[self window] currentEvent]; + NSPoint p = [[self window] mouseLocationOutsideOfEventStream]; + BOOL inKnob = [self testPart: p] == NSScrollerKnob; + + /* clear timer if need be */ + if (inKnob || [scroll_repeat_entry timeInterval] == SCROLL_BAR_FIRST_DELAY) + { + [scroll_repeat_entry invalidate]; + [scroll_repeat_entry release]; + scroll_repeat_entry = nil; + + if (inKnob) + return self; + + scroll_repeat_entry = + [[NSTimer scheduledTimerWithTimeInterval: + SCROLL_BAR_CONTINUOUS_DELAY + target: self + selector: @selector (repeatScroll:) + userInfo: 0 + repeats: YES] + retain]; + } + + [self sendScrollEventAtLoc: 0 fromEvent: e]; + return self; +} + + +/* Asynchronous mouse tracking for scroller. This allows us to dispatch + mouseDragged events without going into a modal loop. */ +- (void)mouseDown: (NSEvent *)e +{ + NSRect sr, kr; + /* hitPart is only updated AFTER event is passed on */ + NSScrollerPart part = [self testPart: [e locationInWindow]]; + double inc = 0.0, loc, kloc, pos; + int edge = 0; + + NSTRACE (EmacsScroller_mouseDown); + + switch (part) + { + case NSScrollerDecrementPage: + last_hit_part = scroll_bar_above_handle; inc = -1.0; break; + case NSScrollerIncrementPage: + last_hit_part = scroll_bar_below_handle; inc = 1.0; break; + case NSScrollerDecrementLine: + last_hit_part = scroll_bar_up_arrow; inc = -0.1; break; + case NSScrollerIncrementLine: + last_hit_part = scroll_bar_down_arrow; inc = 0.1; break; + case NSScrollerKnob: + last_hit_part = scroll_bar_handle; break; + case NSScrollerKnobSlot: /* GNUstep-only */ + last_hit_part = scroll_bar_move_ratio; break; + default: /* NSScrollerNoPart? */ + fprintf (stderr, "EmacsScoller-mouseDown: unexpected part %d\n", part); + return; + } + + if (inc != 0.0) + { + pos = 0; /* ignored */ + + /* set a timer to repeat, as we can't let superclass do this modally */ + scroll_repeat_entry = + [[NSTimer scheduledTimerWithTimeInterval: 0.5 + target: self + selector: @selector (repeatScroll:) + userInfo: 0 + repeats: YES] + retain]; + } + else + { + /* handle, or on GNUstep possibly slot */ + NSEvent *fake_event; + + /* compute float loc in slot and mouse offset on knob */ + sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot] + toView: nil]; + loc = NSHeight (sr) - ([e locationInWindow].y - NSMinY (sr)); + if (loc <= 0.0) + { + loc = 0.0; + edge = -1; + } + else if (loc >= NSHeight (sr)) + { + loc = NSHeight (sr); + edge = 1; + } + + if (edge) + kloc = 0.5 * edge; + else + { + kr = [self convertRect: [self rectForPart: NSScrollerKnob] + toView: nil]; + kloc = NSHeight (kr) - ([e locationInWindow].y - NSMinY (kr)); + } + last_mouse_offset = kloc; + + /* if knob, tell emacs a location offset by knob pos + (to indicate top of handle) */ + if (part == NSScrollerKnob) + pos = (loc - last_mouse_offset) / NSHeight (sr); + else + /* else this is a slot click on GNUstep: go straight there */ + pos = loc / NSHeight (sr); + + /* send a fake mouse-up to super to preempt modal -trackKnob: mode */ + fake_event = [NSEvent mouseEventWithType: NSLeftMouseUp + location: [e locationInWindow] + modifierFlags: [e modifierFlags] + timestamp: [e timestamp] + windowNumber: [e windowNumber] + context: [e context] + eventNumber: [e eventNumber] + clickCount: [e clickCount] + pressure: [e pressure]]; + [super mouseUp: fake_event]; + } + + if (part != NSScrollerKnob) + [self sendScrollEventAtLoc: pos fromEvent: e]; +} + + +/* Called as we manually track scroller drags, rather than superclass. */ +- (void)mouseDragged: (NSEvent *)e +{ + NSRect sr; + double loc, pos; + int edge = 0; + + NSTRACE (EmacsScroller_mouseDragged); + + sr = [self convertRect: [self rectForPart: NSScrollerKnobSlot] + toView: nil]; + loc = NSHeight (sr) - ([e locationInWindow].y - NSMinY (sr)); + + if (loc <= 0.0) + { + loc = 0.0; + edge = -1; + } + else if (loc >= NSHeight (sr) + last_mouse_offset) + { + loc = NSHeight (sr) + last_mouse_offset; + edge = 1; + } + + pos = /*(edge ? loc :*/ (loc - last_mouse_offset) / NSHeight (sr); + [self sendScrollEventAtLoc: pos fromEvent: e]; +} + + +- (void)mouseUp: (NSEvent *)e +{ + if (scroll_repeat_entry) + { + [scroll_repeat_entry invalidate]; + [scroll_repeat_entry release]; + scroll_repeat_entry = nil; + } + last_hit_part = 0; +} + + +/* treat scrollwheel events in the bar as though they were in the main window */ +- (void) scrollWheel: (NSEvent *)theEvent +{ + EmacsView *view = (EmacsView *)FRAME_NS_VIEW (frame); + [view mouseDown: theEvent]; +} + +@end /* EmacsScroller */ + + + +/* ========================================================================== + + EmacsPrefsController implementation + + ========================================================================== */ + + +@implementation EmacsPrefsController + +/* in Tiger+, can just do [popup selectItemWithTag: tag]; */ +static void selectItemWithTag (NSPopUpButton *popup, int tag) +{ + NSEnumerator *items = [[popup itemArray] objectEnumerator]; + NSMenuItem *item; + while (item = [items nextObject]) + { + if ([item tag] == tag) + { + [popup selectItem: item]; + return; + } + } +} + +- init +{ + [NSBundle loadNibNamed: @"preferences" owner: self]; + return self; +} + + +- (void) showForFrame: (struct frame *)f +{ + frame = f; + [self setPanelFromValues]; + [prefsWindow makeKeyAndOrderFront: self]; + [prefsWindow display]; +} + + +- (void) setPanelFromValues +{ + int cursorType = + ns_lisp_to_cursor_type (get_frame_param (frame, Qcursor_type)); + prevExpandSpace = XFLOATINT (ns_expand_space); + prevBlinkRate = NILP (ns_cursor_blink_rate) + ? 0 : XFLOATINT (ns_cursor_blink_rate); + +#ifdef NS_IMPL_COCOA + prevUseHighlightColor = ns_use_system_highlight_color; +#endif + + [expandSpaceSlider setFloatValue: prevExpandSpace]; + [cursorBlinkSlider setFloatValue: prevBlinkRate]; + [cursorTypeMatrix selectCellWithTag: (cursorType == filled_box ? 1 : + (cursorType == bar ? 2 : + (cursorType == underscore ? 3 : 4)))]; + selectItemWithTag (alternateModMenu, lisp_to_mod (ns_alternate_modifier)); + selectItemWithTag (commandModMenu, lisp_to_mod (ns_command_modifier)); +#ifdef NS_IMPL_COCOA + selectItemWithTag (controlModMenu, lisp_to_mod (ns_control_modifier)); + selectItemWithTag (functionModMenu, lisp_to_mod (ns_function_modifier)); + [smoothFontsCheck setState: ns_antialias_text ? YES : NO]; + [useQuickdrawCheck setState: ns_use_qd_smoothing ? YES : NO]; + [useSysHiliteCheck setState: prevUseHighlightColor ? YES : NO]; +#endif +} + + +- (void) setValuesFromPanel +{ + int cursorTag = [[cursorTypeMatrix selectedCell] tag]; + int altTag = [[alternateModMenu selectedItem] tag]; + int cmdTag = [[commandModMenu selectedItem] tag]; +#ifdef NS_IMPL_COCOA + int ctrlTag = [[controlModMenu selectedItem] tag]; + int fnTag = [[functionModMenu selectedItem] tag]; +#endif + float blinkRate = [cursorBlinkSlider floatValue]; + float expandSpace = [expandSpaceSlider floatValue]; + Lisp_Object old_cursor_blink_mode; + + if (expandSpace != prevExpandSpace) + { + ns_expand_space = make_float (expandSpace); + /* PENDING: more needed: store needed metrics in nsfont_info, update + frame default font max_bounds and fontp, recompute faces */ +/* FRAME_LINE_HEIGHT (frame) *= (expandSpace / prevExpandSpace); + x_set_window_size (frame, 0, frame->text_cols, frame->text_lines); */ + prevExpandSpace = expandSpace; + } + if (blinkRate != prevBlinkRate) + { + old_cursor_blink_mode = ns_cursor_blink_mode; + if (blinkRate == 0.0) + { + ns_cursor_blink_rate = Qnil; + ns_cursor_blink_mode = Qnil; + } + else + { + ns_cursor_blink_rate = make_float (blinkRate); + ns_cursor_blink_mode = Qt; + } + if (ns_cursor_blink_mode != old_cursor_blink_mode) + Feval (Fcons (intern ("blink-cursor-mode"), Qnil)); + + if (blinkRate != 0.0 && prevBlinkRate != 0.0) + { /* if changed rates, remove blink handler so change picked up */ + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (frame); + [cursor_blink_entry invalidate]; + [cursor_blink_entry release]; + cursor_blink_entry = 0; + if (dpyinfo->ns_highlight_frame) + { + Lisp_Object tem = + get_frame_param (dpyinfo->ns_highlight_frame, Qcursor_type); + dpyinfo->ns_highlight_frame->output_data.ns->desired_cursor = + ns_lisp_to_cursor_type (tem); + } + } + prevBlinkRate = blinkRate; + } + FRAME_NEW_CURSOR (frame) = + (cursorTag == 1 ? filled_box : + (cursorTag == 2 ? bar : + (cursorTag == 3 ? underscore : hollow_box))); + store_frame_param (frame, Qcursor_type, + ns_cursor_type_to_lisp (FRAME_NEW_CURSOR (frame))); + ns_alternate_modifier = ns_mod_to_lisp (altTag); + ns_command_modifier = ns_mod_to_lisp (cmdTag); +#ifdef NS_IMPL_COCOA + ns_control_modifier = ns_mod_to_lisp (ctrlTag); + ns_function_modifier = ns_mod_to_lisp (fnTag); + ns_antialias_text = [smoothFontsCheck state]; + ns_use_qd_smoothing = [useQuickdrawCheck state]; + ns_use_system_highlight_color = [useSysHiliteCheck state]; + if (ns_use_system_highlight_color != prevUseHighlightColor) + { + prevUseHighlightColor = ns_use_system_highlight_color; + if (ns_use_system_highlight_color == YES) + { + ns_selection_color = [[NSUserDefaults standardUserDefaults] + stringForKey: @"AppleHighlightColor"]; + if (ns_selection_color == nil) + ns_selection_color = NS_SELECTION_COLOR_DEFAULT; + } + else + ns_selection_color = NS_SELECTION_COLOR_DEFAULT; + } +#endif /* NS_IMPL_COCOA */ + Fcall_interactively (intern ("ns-save-preferences"), Qnil, Qnil); +} + + +/* buttons */ +- (IBAction)cancel: (id)sender +{ + [prefsWindow close]; +} + + +- (IBAction)ok: (id)sender +{ + [self setValuesFromPanel]; + [prefsWindow close]; +} + + +- (IBAction)resetToDefaults: (id)sender +{ + ns_set_default_prefs (); + [self setPanelFromValues]; +} + + +- (IBAction)runHelp: (id)sender +{ + Feval (Fcons (intern ("info"), + Fcons (build_string ("(ns-emacs)Preferences Panel"), Qnil))); + SET_FRAME_GARBAGED (frame); + ns_send_appdefined (-1); +} + + +- (IBAction)setColors: (id)sender +{ + Lisp_Object lispFrame; + XSETFRAME (lispFrame, frame); + Fns_popup_color_panel (lispFrame); +} + + +- (IBAction)setDefaultFont: (id)sender +{ + Lisp_Object lispFrame; + XSETFRAME (lispFrame, frame); + Fns_popup_font_panel (lispFrame); +} + +@end /* EmacsPrefsController */ + + + + +/* ========================================================================== + + Font-related functions; these used to be in nsfaces.m + + ========================================================================== */ + + +Lisp_Object +x_new_font (struct frame *f, Lisp_Object font_object, int fontset) +{ + struct font *font = XFONT_OBJECT (font_object); + + if (fontset < 0) + fontset = fontset_from_font (font_object); + FRAME_FONTSET (f) = fontset; + + if (FRAME_FONT (f) == font) + /* This font is already set in frame F. There's nothing more to + do. */ + return font_object; + + FRAME_FONT (f) = font; + + FRAME_BASELINE_OFFSET (f) = font->baseline_offset; + FRAME_COLUMN_WIDTH (f) = font->average_width; + FRAME_SPACE_WIDTH (f) = font->space_width; + FRAME_LINE_HEIGHT (f) = font->height; + + compute_fringe_widths (f, 1); + + /* Compute the scroll bar width in character columns. */ + if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0) + { + int wid = FRAME_COLUMN_WIDTH (f); + FRAME_CONFIG_SCROLL_BAR_COLS (f) + = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid; + } + else + { + int wid = FRAME_COLUMN_WIDTH (f); + FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid; + } + + /* Now make the frame display the given font. */ + if (FRAME_NS_WINDOW (f) != 0) + x_set_window_size (f, 0, FRAME_COLS (f), FRAME_LINES (f)); + + return font_object; +} + + +Lisp_Object +ns_list_fonts (FRAME_PTR f, Lisp_Object pattern, int size, int maxnames) +/* -------------------------------------------------------------------------- + This is used by the xfaces system. It is expected to speak XLFD. + -------------------------------------------------------------------------- */ +{ + Lisp_Object list = Qnil, + rpattern, + key, + tem, + args[2]; + struct re_pattern_buffer *bufp; + id fm = [NSFontManager sharedFontManager]; + NSEnumerator *fenum, *senum; + NSArray *membInfo; + NSString *fontname; + const char *xlfdName; + char *pattFam; + char *patt; + NSString *famName; + + NSTRACE (ns_list_fonts); + + CHECK_STRING (pattern); + patt = XSTRING (pattern)->data; + +#if 0 +/* temporary: for font_backend, we use fontsets, and when these are defined, + the old XLFD-based system is used; eventually this will be replaced by + backend code, but for now we allow specs that are just family names */ + /* if pattern is not XLFD, panic now */ + if (patt[0] != '-') + error ("ns_list_fonts: X font name (XLFD) expected."); + + /* if unicode encoding not requested, also die */ + if (!strstr (patt, "iso10646") && patt[strlen (patt)-3] != '*') + return Qnil; +#endif /* 0 */ + + key = f ? Fcons (pattern, make_number (maxnames)) : Qnil; + tem = f ? XCDR (FRAME_NS_DISPLAY_INFO (f)->name_list_element) : Qnil; + + /* See if we cached the result for this particular query. + The cache is an alist of the form: + ((((PATTERN . MAXNAMES) FONTNAME) ...) ...) + */ + if (f && !NILP (list = Fassoc (key, tem))) + { + list = Fcdr_safe (list); + /* We have a cached list. Don't have to get the list again. */ + if (!NILP (list)) + return list; + } + + if (patt[0] != '-') + pattFam = patt; + else + pattFam = ns_xlfd_to_fontname (patt); + /*PENDING: '*' at beginning matches literally.. */ + if (pattFam[0] == '*') + pattFam[0] = '.'; + + /* must start w/family name, but can have other stuff afterwards + (usually bold and italic specifiers) */ + args[0] = build_string ("^"); + args[1] = build_string (pattFam); + rpattern = Fconcat (2, args); + bufp = compile_pattern (rpattern, 0, Vascii_canon_table, 0, 0); + + list = Qnil; + fenum = [[fm availableFontFamilies] objectEnumerator]; + while ( (famName = [fenum nextObject]) ) + { + NSMutableString *tmp = [famName mutableCopy]; + const char *fname; + NSRange r; + + /* remove spaces, to look like postscript name */ + while ((r = [tmp rangeOfString: @" "]).location != NSNotFound) + [tmp deleteCharactersInRange: r]; + + fname = [tmp UTF8String]; + int len = strlen (fname); + BOOL foundItal; + const char *synthItalFont; + + if (re_search (bufp, fname, len, 0, len, 0) >= 0) + { + /* Found a family. Add all variants. If we have no italic variant, + add a synthItal. */ + senum =[[fm availableMembersOfFontFamily: famName] objectEnumerator]; + foundItal = NO; + synthItalFont = NULL; + while (membInfo = [senum nextObject]) + { + xlfdName = + ns_fontname_to_xlfd ([[membInfo objectAtIndex: 0] UTF8String]); + list = Fcons (build_string (xlfdName), list); + if (!synthItalFont) + { + NSString *synthName = + [[membInfo objectAtIndex: 0] + stringByAppendingString: @"-synthItal"]; + synthItalFont = [synthName UTF8String]; + } + else if ([[membInfo objectAtIndex: 3] intValue] + & NSItalicFontMask) + foundItal = YES; + } + if (foundItal == NO) + { + xlfdName = ns_fontname_to_xlfd (synthItalFont); + list = Fcons (build_string (xlfdName), list); + } + } + [tmp release]; + } + + /* fallback */ + if (XFASTINT (Flength (list)) == 0) + list = Fcons (build_string (ns_fontname_to_xlfd ("Monaco")), list); + + /* store result in cache */ + if (f != NULL) + XCDR_AS_LVALUE (FRAME_NS_DISPLAY_INFO (f)->name_list_element) + = Fcons (Fcons (key, list), + XCDR (FRAME_NS_DISPLAY_INFO (f)->name_list_element)); + return list; +} + + +/* XLFD: -foundry-family-weight-slant-swidth-adstyle-pxlsz-ptSz-resx-resy-spc-avgWidth-rgstry-encoding */ + +const char * +ns_font_to_xlfd (NSFont *nsfont) +/* -------------------------------------------------------------------------- + Convert an NS font name to an X font name (XLFD). + The string returned is temporarily allocated. + -------------------------------------------------------------------------- */ +{ + NSFontManager *mgr = [NSFontManager sharedFontManager]; + NSString *sname = [nsfont /*familyName*/fontName]; + char *famName = [sname UTF8String]; + char *weightStr = [mgr fontNamed: sname hasTraits: NSBoldFontMask] ? + "bold" : "medium"; + char *slantStr = [mgr fontNamed: sname hasTraits: NSItalicFontMask] ? + "i" : "r"; + int size = [nsfont pointSize]; + int aWidth = lrint (10.0 * [nsfont widthOfString: @"a"]); + const char *xlfd; + int i, len; + + /* change '-' to '$' to avoid messing w/XLFD separator */ + for (len =strlen (famName), i =0; i<len; i++) + if (famName[i] == '-') + { + famName[i] = '\0'; + break; + } + + xlfd = [[NSString stringWithFormat: + @"-apple-%s-%s-%s-normal--%d-%d-75-75-m-%d-iso10646-1", + famName, weightStr, slantStr, size, 10*size, aWidth] + UTF8String]; +/*fprintf (stderr, "converted '%s' to '%s'\n",name,xlfd); */ + return xlfd; +} + +const char * +ns_fontname_to_xlfd (const char *name) +/* -------------------------------------------------------------------------- + Convert an NS font name to an X font name (XLFD). + Sizes are set to 0. + The string returned is temporarily allocated. + -------------------------------------------------------------------------- */ +{ + char famName[180]; + char *weightStr = strcasestr (name, "bold") ? "bold" : "medium"; + char *slantStr = strcasestr (name, "italic") || strcasestr (name, "oblique") + || strcasestr (name, "synthital") ? "i" : "r"; + int i, len; + const char *xlfd; + + /* change '-' to '$' to avoid messing w/XLFD separator, and ' ' to '_' */ + bzero (famName, 180); + bcopy (name, famName, max (strlen (name), 179)); + for (len =strlen (famName), i =0; i<len; i++) + { + if (famName[i] == '-') + famName[i] = '$'; + else if (famName[i] == ' ') + famName[i] = '_'; + } + + xlfd = [[NSString stringWithFormat: + @"-apple-%s-%s-%s-normal--0-0-75-75-m-0-iso10646-1", + famName, weightStr, slantStr] + UTF8String]; +/*fprintf (stderr, "converted '%s' to '%s'\n",name,xlfd); */ + return xlfd; +} + + +const char * +ns_xlfd_to_fontname (const char *xlfd) +/* -------------------------------------------------------------------------- + Convert an X font name (XLFD) to an NS font name. + Only family is used. + The string returned is temporarily allocated. + -------------------------------------------------------------------------- */ +{ + char *name = xmalloc (180); + int i, len; + const char *ret; + + if (!strncmp (xlfd, "--", 2)) + sscanf (xlfd, "--%*[^-]-%[^-]179-", name); + else + sscanf (xlfd, "-%*[^-]-%[^-]179-", name); + + /* stopgap for malformed XLFD input */ + if (strlen (name) == 0) + strcpy (name, "Monaco"); + + /* undo hack in ns_fontname_to_xlfd, converting '$' to '-', '_' to ' ' + also uppercase after '-' or ' ' */ + name[0] = toupper (name[0]); + for (len =strlen (name), i =0; i<len; i++) + { + if (name[i] == '$') + { + name[i] = '-'; + if (i+1<len) + name[i+1] = toupper (name[i+1]); + } + else if (name[i] == '_') + { + name[i] = ' '; + if (i+1<len) + name[i+1] = toupper (name[i+1]); + } + } +/*fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name); */ + ret = [[NSString stringWithUTF8String: name] UTF8String]; + xfree (name); + return ret; +} diff --git a/src/process.c b/src/process.c index c2a20b92db1..87772f34f75 100644 --- a/src/process.c +++ b/src/process.c @@ -4877,8 +4877,12 @@ wait_reading_process_output (time_limit, microsecs, read_kbd, do_display, process_output_skip = 0; } #endif - - nfds = select (max (max (max_process_desc, max_keyboard_desc), +#ifdef HAVE_NS + nfds = ns_select +#else + nfds = select +#endif + (max (max (max_process_desc, max_keyboard_desc), max_gpm_desc) + 1, &Available, #ifdef NON_BLOCKING_CONNECT diff --git a/src/s/darwin.h b/src/s/darwin.h index 4e282e99a9d..a290188d337 100644 --- a/src/s/darwin.h +++ b/src/s/darwin.h @@ -221,7 +221,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ /* In Carbon, asynchronous I/O (using SIGIO) can't be used for window events because they don't come from sockets, even though it works fine on tty's. */ -#ifdef HAVE_CARBON +/* This seems to help in Ctrl-G detection under Cocoa, however at the cost + of some quirks that may or may not bother a given user. */ +#if defined (HAVE_CARBON) || defined (COCOA_EXPERIMENTAL_CTRL_G) #define NO_SOCK_SIGIO #endif @@ -247,8 +249,21 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ /* Definitions for how to compile & link. */ -/* Indicate that we are compiling for Mac OS X. */ +/* This is for the Carbon port. Under the NeXTstep port, this is still picked + up during preprocessing, but is undone in config.in. */ +#ifndef HAVE_NS #define C_SWITCH_SYSTEM -fpascal-strings -DMAC_OSX +#endif + +/* Link in the Carbon or AppKit lib. */ +#ifdef HAVE_NS +/* PENDING: lresolv is here because configure when testing #undefs res_init, + a macro in /usr/include/resolv.h for res_9_init, not in stdc lib. */ +#define LIBS_MACGUI -framework AppKit -lresolv +#define SYSTEM_PURESIZE_EXTRA 200000 +#define HEADERPAD_EXTRA 6C8 +#else +#define HEADERPAD_EXTRA 690 #ifdef HAVE_CARBON @@ -274,16 +289,17 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ /* Link in the Carbon lib. */ #ifdef HAVE_CARBON -#define LIBS_CARBON -framework Carbon LIBS_IMAGE +#define LIBS_MACGUI -framework Carbon LIBS_IMAGE #else -#define LIBS_CARBON -#endif +#define LIBS_MACGUI +#endif /* !HAVE_CARBON */ +#endif /* !HAVE_NS */ /* The -headerpad option tells ld (see man page) to leave room at the end of the header for adding load commands. Needed for dumping. 0x690 is the total size of 30 segment load commands (at 56 - each). */ -#define LD_SWITCH_SYSTEM_TEMACS -prebind LIBS_CARBON -Xlinker -headerpad -Xlinker 690 + each); under Cocoa 31 commands are required. */ +#define LD_SWITCH_SYSTEM_TEMACS -prebind LIBS_MACGUI -Xlinker -headerpad -Xlinker HEADERPAD_EXTRA #define C_SWITCH_SYSTEM_TEMACS -Dtemacs @@ -312,6 +328,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #define OTHER_FILES macosx-app #endif +/* PENDING: can this target be specified in a clearer way? */ +#ifdef HAVE_NS +#define OTHER_FILES ns-app +#endif + /* Define the following so emacs symbols will not conflict with those in the System framework. Otherwise -prebind will not work. */ diff --git a/src/syntax.c b/src/syntax.c index 6dc63c25537..0c547c724c9 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -1,3 +1,4 @@ +#include <stdio.h> /* GNU Emacs routines to deal with syntax tables; also word and list parsing. Copyright (C) 1985, 1987, 1993, 1994, 1995, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 @@ -298,6 +299,18 @@ char_quoted (charpos, bytepos) register int quoted = 0; int orig = charpos; +#ifdef HAVE_NS + /* For some reason keeps getting called w/both 1, then segfaulting + due to the definitions of DEC_BOTH and DEC_POS in character.h, + which lead to decrementing below initial address and then examining + character there. Need to investigate further.. */ + if (charpos < 2 || bytepos < 2) + { + //fprintf(stderr,"Returning because charpos = %d, bytepos = %d\n",charpos, bytepos); + return 0; + } +#endif + DEC_BOTH (charpos, bytepos); while (charpos >= beg) diff --git a/src/sysselect.h b/src/sysselect.h index 24731e56f1f..e11d5ce04ac 100644 --- a/src/sysselect.h +++ b/src/sysselect.h @@ -18,11 +18,11 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #ifdef HAVE_SYS_SELECT_H -#if defined (DARWIN) || defined (MAC_OSX) +#if defined (DARWIN) || defined (MAC_OSX) || defined (NS_IMPL_COCOA) #undef init_process #endif #include <sys/select.h> -#if defined (DARWIN) || defined (MAC_OSX) +#if defined (DARWIN) || defined (MAC_OSX) || defined (NS_IMPL_COCOA) #define init_process emacs_init_process #endif #endif diff --git a/src/termhooks.h b/src/termhooks.h index ae4fb361e36..1be9a1b45b9 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -318,6 +318,8 @@ extern struct tty_display_info *gpm_tty; struct mac_display_info; +struct ns_display_info; +struct x_display_info; struct w32_display_info; /* Terminal-local parameters. */ @@ -368,6 +370,7 @@ struct terminal struct x_display_info *x; /* xterm.h */ struct w32_display_info *w32; /* w32term.h */ struct mac_display_info *mac; /* macterm.h */ + struct ns_display_info *ns; /* nsterm.h */ } display_info; diff --git a/src/terminal.c b/src/terminal.c index afb350ba310..9ec14ca3c1f 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -397,6 +397,8 @@ possible return values. */) return Qpc; case output_mac: return Qmac; + case output_ns: + return Qns; default: abort (); } diff --git a/src/terminfo.c b/src/terminfo.c index c1e1a36a833..968d338279b 100644 --- a/src/terminfo.c +++ b/src/terminfo.c @@ -24,7 +24,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ so that we do not need to conditionalize the places in Emacs that set them. */ +/* Causes a conflict on OS X 10.3 .*/ +#ifndef NS_IMPL_COCOA char *UP, *BC, PC; +#endif /* Interface to curses/terminfo library. Turns out that all of the terminfo-level routines look diff --git a/src/w32gui.h b/src/w32gui.h index aea3582b3ef..d9f09e33134 100644 --- a/src/w32gui.h +++ b/src/w32gui.h @@ -21,66 +21,19 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #define EMACS_W32GUI_H #include <windows.h> +#include "w32bdf.h" -/* Emulate widget_value from ../lwlib/lwlib.h, modified for Windows. */ -typedef void * XtPointer; -typedef char Boolean; -enum button_type +/* Emulate XCharStruct. */ +typedef struct _XCharStruct { - BUTTON_TYPE_NONE, - BUTTON_TYPE_TOGGLE, - BUTTON_TYPE_RADIO -}; -typedef struct _widget_value -{ - /* name of widget */ - Lisp_Object lname; - char* name; - /* value (meaning depend on widget type) */ - char* value; - /* keyboard equivalent. no implications for XtTranslations */ - Lisp_Object lkey; - char* key; - /* Help string or nil if none. - GC finds this string through the frame's menu_bar_vector - or through menu_items. */ - Lisp_Object help; - /* true if enabled */ - Boolean enabled; - /* true if selected */ - Boolean selected; - /* The type of a button. */ - enum button_type button_type; - /* true if menu title */ - Boolean title; -#if 0 - /* true if was edited (maintained by get_value) */ - Boolean edited; - /* true if has changed (maintained by lw library) */ - change_type change; - /* true if this widget itself has changed, - but not counting the other widgets found in the `next' field. */ - change_type this_one_change; -#endif - /* Contents of the sub-widgets, also selected slot for checkbox */ - struct _widget_value* contents; - /* data passed to callback */ - XtPointer call_data; - /* next one in the list */ - struct _widget_value* next; -#if 0 - /* slot for the toolkit dependent part. Always initialize to NULL. */ - void* toolkit_data; - /* tell us if we should free the toolkit data slot when freeing the - widget_value itself. */ - Boolean free_toolkit_data; - - /* we resource the widget_value structures; this points to the next - one on the free list if this one has been deallocated. - */ - struct _widget_value *free_list; -#endif -} widget_value; + short rbearing; + short lbearing; + short width; + short ascent; + short descent; +} XCharStruct; + + /* Local memory management for menus. */ #define local_heap (GetProcessHeap ()) #define local_alloc(n) (HeapAlloc (local_heap, HEAP_ZERO_MEMORY, (n))) @@ -89,6 +42,30 @@ typedef struct _widget_value #define malloc_widget_value() ((widget_value *) local_alloc (sizeof (widget_value))) #define free_widget_value(wv) (local_free ((wv))) + +enum w32_char_font_type +{ + UNKNOWN_FONT = 0 /* FONT_TYPE_UNKNOWN */, + ANSI_FONT, + UNICODE_FONT, + BDF_1D_FONT, + BDF_2D_FONT +}; + +typedef struct W32FontStruct { + enum w32_char_font_type font_type; + TEXTMETRIC tm; + HFONT hfont; + bdffont *bdf; + int double_byte_p; + XCharStruct max_bounds; + XCharStruct scratch; + /* Only store info for ascii chars, if not fixed pitch. */ + XCharStruct * per_char; +} W32FontStruct; + +typedef struct W32FontStruct XFontStruct; + /* Emulate X GC's by keeping color and font info in a structure. */ typedef struct _XGCValues { diff --git a/src/window.c b/src/window.c index 43b96534427..753ed50a20f 100644 --- a/src/window.c +++ b/src/window.c @@ -49,6 +49,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #ifdef MAC_OS #include "macterm.h" #endif +#ifdef HAVE_NS +#include "nsterm.h" +#endif Lisp_Object Qwindowp, Qwindow_live_p, Qwindow_configuration_p; diff --git a/src/xdisp.c b/src/xdisp.c index d6a96c4a72f..43cfaee767b 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -199,6 +199,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #ifdef MAC_OS #include "macterm.h" #endif +#ifdef HAVE_NS +#include "nsterm.h" +#endif #include "font.h" @@ -209,7 +212,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #define INFINITY 10000000 #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined(HAVE_NS) || defined (USE_GTK) extern void set_frame_menubar P_ ((struct frame *f, int, int)); extern int pending_menu_activation; #endif @@ -861,7 +864,7 @@ int display_hourglass_p; int hourglass_shown_p; /* If non-null, an asynchronous timer that, when it expires, displays - an hourglass cursor on all frames. */ + an hourglass cursor on all frames. */ struct atimer *hourglass_atimer; /* Number of seconds to wait before displaying an hourglass cursor. */ @@ -9462,7 +9465,32 @@ x_consider_frame_title (frame) if (! STRINGP (f->name) || SBYTES (f->name) != len || bcmp (title, SDATA (f->name), len) != 0) - x_implicitly_set_name (f, make_string (title, len), Qnil); + { +#ifdef HAVE_NS + if (FRAME_NS_P (f)) + { + if (!MINI_WINDOW_P(XWINDOW(f->selected_window))) + { + if (EQ (fmt, Qt)) + ns_set_name_as_filename (f); + else + x_implicitly_set_name (f, make_string(title, len), + Qnil); + } + } + else +#endif + x_implicitly_set_name (f, make_string (title, len), Qnil); + } +#ifdef HAVE_NS + if (FRAME_NS_P (f)) + { + /* do this also for frames with explicit names */ + ns_implicitly_set_icon_type(f); + ns_set_doc_edited(f, Fbuffer_modified_p + (XWINDOW (f->selected_window)->buffer), Qnil); + } +#endif } } @@ -9627,7 +9655,7 @@ update_menu_bar (f, save_match_data, hooks_run) if (FRAME_WINDOW_P (f) ? #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined (HAVE_NS) || defined (USE_GTK) FRAME_EXTERNAL_MENU_BAR (f) #else FRAME_MENU_BAR_LINES (f) > 0 @@ -9686,10 +9714,10 @@ update_menu_bar (f, save_match_data, hooks_run) /* Redisplay the menu bar in case we changed it. */ #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined (HAVE_NS) || defined (USE_GTK) if (FRAME_WINDOW_P (f)) - { -#ifdef MAC_OS + { +#if defined (MAC_OS) || defined (HAVE_NS) /* All frames on Mac OS share the same menubar. So only the selected frame should be allowed to set it. */ if (f == SELECTED_FRAME ()) @@ -9700,11 +9728,11 @@ update_menu_bar (f, save_match_data, hooks_run) /* On a terminal screen, the menu bar is an ordinary screen line, and this makes it get updated. */ w->update_mode_line = Qt; -#else /* ! (USE_X_TOOLKIT || HAVE_NTGUI || MAC_OS || USE_GTK) */ +#else /* ! (USE_X_TOOLKIT || HAVE_NTGUI || MAC_OS || HAVE_NS || USE_GTK) */ /* In the non-toolkit version, the menu bar is an ordinary screen line, and this makes it get updated. */ w->update_mode_line = Qt; -#endif /* ! (USE_X_TOOLKIT || HAVE_NTGUI || MAC_OS || USE_GTK) */ +#endif /* ! (USE_X_TOOLKIT || HAVE_NTGUI || MAC_OS || HAVE_NS || USE_GTK) */ unbind_to (count, Qnil); set_buffer_internal_1 (prev); @@ -9815,7 +9843,7 @@ update_tool_bar (f, save_match_data) struct frame *f; int save_match_data; { -#if defined (USE_GTK) || USE_MAC_TOOLBAR +#if defined (USE_GTK) || defined (HAVE_NS) || USE_MAC_TOOLBAR int do_update = FRAME_EXTERNAL_TOOL_BAR (f); #else int do_update = WINDOWP (f->tool_bar_window) @@ -10281,7 +10309,7 @@ redisplay_tool_bar (f) struct it it; struct glyph_row *row; -#if defined (USE_GTK) || USE_MAC_TOOLBAR +#if defined (USE_GTK) || defined (HAVE_NS) || USE_MAC_TOOLBAR if (FRAME_EXTERNAL_TOOL_BAR (f)) update_frame_tool_bar (f); return 0; @@ -11501,6 +11529,10 @@ redisplay_internal (preserve_echo_area) /* Resized active mini-window to fit the size of what it is showing if its contents might have changed. */ must_finish = 1; +/* PENDING: this causes all frames to be updated, which seems unnecessary + since only the current frame needs to be considered. This function needs + to be rewritten with two variables, consider_all_windows and + consider_all_frames. */ consider_all_windows_p = 1; ++windows_or_buffers_changed; ++update_mode_lines; @@ -13913,7 +13945,7 @@ redisplay_window (window, just_this_one_p) if (FRAME_WINDOW_P (f)) { #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) || defined (MAC_OS) \ - || defined (USE_GTK) + || defined (HAVE_NS) || defined (USE_GTK) redisplay_menu_p = FRAME_EXTERNAL_MENU_BAR (f); #else redisplay_menu_p = FRAME_MENU_BAR_LINES (f) > 0; @@ -13928,7 +13960,7 @@ redisplay_window (window, just_this_one_p) #ifdef HAVE_WINDOW_SYSTEM if (FRAME_WINDOW_P (f)) { -#if defined (USE_GTK) || USE_MAC_TOOLBAR +#if defined (USE_GTK) || defined (HAVE_NS) || USE_MAC_TOOLBAR redisplay_tool_bar_p = FRAME_EXTERNAL_TOOL_BAR (f); #else redisplay_tool_bar_p = WINDOWP (f->tool_bar_window) @@ -17072,6 +17104,11 @@ display_menu_bar (w) return; #endif +#ifdef HAVE_NS + if (FRAME_NS_P (f)) + return; +#endif /* HAVE_NS */ + #ifdef USE_X_TOOLKIT xassert (!FRAME_WINDOW_P (f)); init_iterator (&it, w, -1, -1, f->desired_matrix->rows, MENU_FACE_ID); @@ -22539,7 +22576,10 @@ display_and_set_cursor (w, on, hpos, vpos, x, y) /* Switch the display of W's cursor on or off, according to the value of ON. */ -static void +#ifndef HAVE_NS +static +#endif +void update_window_cursor (w, on) struct window *w; int on; diff --git a/src/xfaces.c b/src/xfaces.c index 87a9006ae22..4bdcd1e406e 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -244,6 +244,17 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #define check_x check_mac #endif /* MAC_OS */ +#ifdef HAVE_NS +#include "nsterm.h" +#undef FRAME_X_DISPLAY_INFO +#define FRAME_X_DISPLAY_INFO FRAME_NS_DISPLAY_INFO +#define x_display_info ns_display_info +#define FRAME_X_FONT_TABLE FRAME_NS_FONT_TABLE +#define check_x check_ns +#define x_list_fonts ns_list_fonts +#define GCGraphicsExposures 0 +#endif /* HAVE_NS */ + #include "buffer.h" #include "dispextern.h" #include "blockinput.h" @@ -556,6 +567,10 @@ static void x_free_gc P_ ((struct frame *, GC)); extern Lisp_Object w32_list_fonts P_ ((struct frame *, Lisp_Object, int, int)); #endif /* WINDOWSNT */ +#ifdef HAVE_NS +extern Lisp_Object ns_list_fonts P_ ((struct frame *, Lisp_Object, int, int)); +#endif /* HAVE_NS */ + #ifdef USE_X_TOOLKIT static void x_update_menu_appearance P_ ((struct frame *)); @@ -766,6 +781,31 @@ x_free_gc (f, gc) #endif /* WINDOWSNT */ +#ifdef HAVE_NS +/* NS emulation of GCs */ + +static INLINE GC +x_create_gc (f, mask, xgcv) + struct frame *f; + unsigned long mask; + XGCValues *xgcv; +{ + GC gc = xmalloc (sizeof (*gc)); + if (gc) + bcopy(xgcv, gc, sizeof(XGCValues)); + return gc; +} + +static INLINE void +x_free_gc (f, gc) + struct frame *f; + GC gc; +{ + if (gc) + xfree (gc); +} +#endif /* HAVE_NS */ + #ifdef MAC_OS /* Mac OS emulation of GCs */ @@ -872,8 +912,11 @@ init_frame_faces (f) #ifdef MAC_OS if (!FRAME_MAC_P (f) || FRAME_MAC_WINDOW (f)) #endif +#ifdef HAVE_NS + if (!FRAME_NS_P (f) || FRAME_NS_WINDOW (f)) +#endif if (!realize_basic_faces (f)) - abort (); + abort (); } @@ -1269,6 +1312,10 @@ defined_color (f, color_name, color_def, alloc) else if (FRAME_MAC_P (f)) return mac_defined_color (f, color_name, color_def, alloc); #endif +#ifdef HAVE_NS + else if (FRAME_NS_P (f)) + return ns_defined_color (f, color_name, color_def, alloc, 1); +#endif else abort (); } @@ -1558,6 +1605,7 @@ free_face_colors (f, face) struct frame *f; struct face *face; { +/* PENDING(NS): need to do something here? */ #ifdef HAVE_X_WINDOWS if (face->colors_copied_bitwise_p) return; @@ -2754,7 +2802,7 @@ merge_face_ref (f, face_ref, to, err_msgs, named_merge_points) } else if (EQ (keyword, QCstipple)) { -#ifdef HAVE_X_WINDOWS +#if defined(HAVE_X_WINDOWS) || defined(HAVE_NS) Lisp_Object pixmap_p = Fbitmap_spec_p (value); if (!NILP (pixmap_p)) to[LFACE_STIPPLE_INDEX] = value; @@ -3262,14 +3310,14 @@ FRAME 0 means change the face on all frames, and change the default } else if (EQ (attr, QCstipple)) { -#ifdef HAVE_X_WINDOWS +#if defined(HAVE_X_WINDOWS) || defined(HAVE_NS) if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value) && !NILP (value) && NILP (Fbitmap_spec_p (value))) signal_error ("Invalid stipple attribute", value); old_value = LFACE_STIPPLE (lface); LFACE_STIPPLE (lface) = value; -#endif /* HAVE_X_WINDOWS */ +#endif /* HAVE_X_WINDOWS || HAVE_NS */ } else if (EQ (attr, QCwidth)) { |