/* Implementation of GUI terminal on the Mac OS.
Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 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 of the License, 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. If not, see . */
/* Contributed by Andrew Choi (akochoi@mac.com). */
#include
#include
#include
#include "lisp.h"
#include "blockinput.h"
#include "macterm.h"
#ifndef MAC_OSX
#include
#endif
#if !TARGET_API_MAC_CARBON
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined (__MRC__) || (__MSL__ >= 0x6000)
#include
#endif
#if __profile__
#include
#endif
#endif /* not TARGET_API_MAC_CARBON */
#include "systty.h"
#include "systime.h"
#include
#include
#include
#include
#include "charset.h"
#include "coding.h"
#include "frame.h"
#include "dispextern.h"
#include "fontset.h"
#include "termhooks.h"
#include "termopts.h"
#include "termchar.h"
#include "disptab.h"
#include "buffer.h"
#include "window.h"
#include "keyboard.h"
#include "intervals.h"
#include "atimer.h"
#include "keymap.h"
#include "character.h"
#include "ccl.h"
/* Non-nil means Emacs uses toolkit scroll bars. */
Lisp_Object Vx_toolkit_scroll_bars;
/* If non-zero, the text will be rendered using Core Graphics text
rendering which may anti-alias the text. */
int mac_use_core_graphics;
/* Non-zero means that a HELP_EVENT has been generated since Emacs
start. */
static int any_help_event_p;
/* Last window where we saw the mouse. Used by mouse-autoselect-window. */
static Lisp_Object last_window;
/* Non-zero means make use of UNDERLINE_POSITION font properties.
(Not yet supported.) */
int x_use_underline_position_properties;
/* Non-zero means to draw the underline at the same place as the descent line. */
int x_underline_at_descent_line;
/* This is a chain of structures for all the X displays currently in
use. */
struct x_display_info *x_display_list;
/* This is a list of cons cells, each of the form (NAME
FONT-LIST-CACHE . RESOURCE-DATABASE), one for each element of
x_display_list and in the same order. NAME is the name of the
frame. FONT-LIST-CACHE records previous values returned by
x-list-fonts. RESOURCE-DATABASE preserves the X Resource Database
equivalent, which is implemented with a Lisp object, for the
display. */
Lisp_Object x_display_name_list;
/* This is display since Mac does not support multiple ones. */
struct mac_display_info one_mac_display_info;
/* Frame being updated by update_frame. This is declared in term.c.
This is set by update_begin and looked at by all the XT functions.
It is zero while not inside an update. In that case, the XT
functions assume that `selected_frame' is the frame to apply to. */
extern struct frame *updating_frame;
/* This is a frame waiting to be auto-raised, within XTread_socket. */
struct frame *pending_autoraise_frame;
/* Mouse movement.
Formerly, we used PointerMotionHintMask (in standard_event_mask)
so that we would have to call XQueryPointer after each MotionNotify
event to ask for another such event. However, this made mouse tracking
slow, and there was a bug that made it eventually stop.
Simply asking for MotionNotify all the time seems to work better.
In order to avoid asking for motion events and then throwing most
of them away or busy-polling the server for mouse positions, we ask
the server for pointer motion hints. This means that we get only
one event per group of mouse movements. "Groups" are delimited by
other kinds of events (focus changes and button clicks, for
example), or by XQueryPointer calls; when one of these happens, we
get another MotionNotify event the next time the mouse moves. This
is at least as efficient as getting motion events when mouse
tracking is on, and I suspect only negligibly worse when tracking
is off. */
/* Where the mouse was last time we reported a mouse event. */
static Rect last_mouse_glyph;
static FRAME_PTR last_mouse_glyph_frame;
/* The scroll bar in which the last X motion event occurred.
If the last X motion event occurred in a scroll bar, we set this so
XTmouse_position can know whether to report a scroll bar motion or
an ordinary motion.
If the last X motion event didn't occur in a scroll bar, we set
this to Qnil, to tell XTmouse_position to return an ordinary motion
event. */
static Lisp_Object last_mouse_scroll_bar;
/* This is a hack. We would really prefer that XTmouse_position would
return the time associated with the position it returns, but there
doesn't seem to be any way to wrest the time-stamp from the server
along with the position query. So, we just keep track of the time
of the last movement we received, and return that in hopes that
it's somewhat accurate. */
static Time last_mouse_movement_time;
struct scroll_bar *tracked_scroll_bar = NULL;
/* Incremented by XTread_socket whenever it really tries to read
events. */
#ifdef __STDC__
static int volatile input_signal_count;
#else
static int input_signal_count;
#endif
extern Lisp_Object Vsystem_name;
extern Lisp_Object Qeql;
/* A mask of extra modifier bits to put into every keyboard char. */
extern EMACS_INT extra_keyboard_modifiers;
/* The keysyms to use for the various modifiers. */
static Lisp_Object Qalt, Qhyper, Qsuper, Qcontrol, Qmeta, Qmodifier_value;
extern int inhibit_window_system;
#if __MRC__ && !TARGET_API_MAC_CARBON
QDGlobals qd; /* QuickDraw global information structure. */
#endif
#define mac_window_to_frame(wp) (((mac_output *) GetWRefCon (wp))->mFP)
struct mac_display_info *mac_display_info_for_display (Display *);
static void x_update_window_end P_ ((struct window *, int, int));
int x_catch_errors P_ ((Display *));
void x_uncatch_errors P_ ((Display *, int));
void x_lower_frame P_ ((struct frame *));
void x_scroll_bar_clear P_ ((struct frame *));
int x_had_errors_p P_ ((Display *));
void x_wm_set_size_hint P_ ((struct frame *, long, int));
void x_raise_frame P_ ((struct frame *));
void x_set_window_size P_ ((struct frame *, int, int, int));
void x_wm_set_window_state P_ ((struct frame *, int));
void x_wm_set_icon_pixmap P_ ((struct frame *, int));
static void mac_initialize P_ ((void));
static void x_font_min_bounds P_ ((XFontStruct *, int *, int *));
static int x_compute_min_glyph_bounds P_ ((struct frame *));
static void x_update_end P_ ((struct frame *));
static void XTframe_up_to_date P_ ((struct frame *));
static void XTset_terminal_modes P_ ((struct terminal *));
static void XTreset_terminal_modes P_ ((struct terminal *));
static void x_clear_frame P_ ((struct frame *));
static void frame_highlight P_ ((struct frame *));
static void frame_unhighlight P_ ((struct frame *));
static void x_new_focus_frame P_ ((struct x_display_info *, struct frame *));
static void mac_focus_changed P_ ((int, struct mac_display_info *,
struct frame *, struct input_event *));
static void x_detect_focus_change P_ ((struct mac_display_info *,
const EventRecord *,
struct input_event *));
static void XTframe_rehighlight P_ ((struct frame *));
static void x_frame_rehighlight P_ ((struct x_display_info *));
static void x_draw_hollow_cursor P_ ((struct window *, struct glyph_row *));
static void x_draw_bar_cursor P_ ((struct window *, struct glyph_row *, int,
enum text_cursor_kinds));
static void x_clip_to_row P_ ((struct window *, struct glyph_row *, int, GC));
static void x_flush P_ ((struct frame *f));
static void x_update_begin P_ ((struct frame *));
static void x_update_window_begin P_ ((struct window *));
static void x_after_update_window_line P_ ((struct glyph_row *));
static void x_scroll_bar_report_motion P_ ((struct frame **, Lisp_Object *,
enum scroll_bar_part *,
Lisp_Object *, Lisp_Object *,
unsigned long *));
static int is_emacs_window P_ ((WindowRef));
static XCharStruct *mac_per_char_metric P_ ((XFontStruct *, XChar2b *, int));
static void XSetFont P_ ((Display *, GC, XFontStruct *));
static struct terminal *mac_create_terminal P_ ((struct mac_display_info *dpyinfo));
#define GC_FORE_COLOR(gc) (&(gc)->fore_color)
#define GC_BACK_COLOR(gc) (&(gc)->back_color)
#define GC_FONT(gc) ((gc)->xgcv.font)
#define FRAME_NORMAL_GC(f) ((f)->output_data.mac->normal_gc)
#define CG_SET_FILL_COLOR(context, color) \
CGContextSetRGBFillColor (context, \
RED_FROM_ULONG (color) / 255.0f, \
GREEN_FROM_ULONG (color) / 255.0f, \
BLUE_FROM_ULONG (color) / 255.0f, 1.0f)
#if USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
#define CG_SET_FILL_COLOR_MAYBE_WITH_CGCOLOR(context, color, cg_color) \
do { \
if (CGColorGetTypeID != NULL) \
CGContextSetFillColorWithColor (context, cg_color); \
else \
CG_SET_FILL_COLOR (context, color); \
} while (0)
#else
#define CG_SET_FILL_COLOR_MAYBE_WITH_CGCOLOR(context, color, cg_color) \
CGContextSetFillColorWithColor (context, cg_color)
#endif
#else
#define CG_SET_FILL_COLOR_MAYBE_WITH_CGCOLOR(context, color, cg_color) \
CG_SET_FILL_COLOR (context, color)
#endif
#define CG_SET_FILL_COLOR_WITH_GC_FOREGROUND(context, gc) \
CG_SET_FILL_COLOR_MAYBE_WITH_CGCOLOR (context, (gc)->xgcv.foreground, \
(gc)->cg_fore_color)
#define CG_SET_FILL_COLOR_WITH_GC_BACKGROUND(context, gc) \
CG_SET_FILL_COLOR_MAYBE_WITH_CGCOLOR (context, (gc)->xgcv.background, \
(gc)->cg_back_color)
#define CG_SET_STROKE_COLOR(context, color) \
CGContextSetRGBStrokeColor (context, \
RED_FROM_ULONG (color) / 255.0f, \
GREEN_FROM_ULONG (color) / 255.0f, \
BLUE_FROM_ULONG (color) / 255.0f, 1.0f)
#if USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
#define CG_SET_STROKE_COLOR_MAYBE_WITH_CGCOLOR(context, color, cg_color) \
do { \
if (CGColorGetTypeID != NULL) \
CGContextSetStrokeColorWithColor (context, cg_color); \
else \
CG_SET_STROKE_COLOR (context, color); \
} while (0)
#else
#define CG_SET_STROKE_COLOR_MAYBE_WITH_CGCOLOR(context, color, cg_color) \
CGContextSetStrokeColorWithColor (context, cg_color)
#endif
#else
#define CG_SET_STROKE_COLOR_MAYBE_WITH_CGCOLOR(context, color, cg_color) \
CG_SET_STROKE_COLOR (context, color)
#endif
#define CG_SET_STROKE_COLOR_WITH_GC_FOREGROUND(context, gc) \
CG_SET_STROKE_COLOR_MAYBE_WITH_CGCOLOR (context, (gc)->xgcv.foreground, \
(gc)->cg_fore_color)
#if USE_CG_DRAWING
#define FRAME_CG_CONTEXT(f) ((f)->output_data.mac->cg_context)
/* Fringe bitmaps. */
static int max_fringe_bmp = 0;
static CGImageRef *fringe_bmp = 0;
CGColorSpaceRef mac_cg_color_space_rgb;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
static CGColorRef mac_cg_color_black;
#endif
static void
init_cg_color ()
{
mac_cg_color_space_rgb = CGColorSpaceCreateDeviceRGB ();
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
/* Don't check the availability of CGColorCreate; this symbol is
defined even in Mac OS X 10.1. */
if (CGColorGetTypeID != NULL)
#endif
{
CGFloat rgba[] = {0.0f, 0.0f, 0.0f, 1.0f};
mac_cg_color_black = CGColorCreate (mac_cg_color_space_rgb, rgba);
}
#endif
}
static CGContextRef
mac_begin_cg_clip (f, gc)
struct frame *f;
GC gc;
{
CGContextRef context = FRAME_CG_CONTEXT (f);
if (!context)
{
QDBeginCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)), &context);
FRAME_CG_CONTEXT (f) = context;
}
CGContextSaveGState (context);
CGContextTranslateCTM (context, 0, FRAME_PIXEL_HEIGHT (f));
CGContextScaleCTM (context, 1, -1);
if (gc && gc->n_clip_rects)
CGContextClipToRects (context, gc->clip_rects, gc->n_clip_rects);
return context;
}
static void
mac_end_cg_clip (f)
struct frame *f;
{
CGContextRestoreGState (FRAME_CG_CONTEXT (f));
}
void
mac_prepare_for_quickdraw (f)
struct frame *f;
{
if (f == NULL)
{
Lisp_Object rest, frame;
FOR_EACH_FRAME (rest, frame)
if (FRAME_MAC_P (XFRAME (frame)))
mac_prepare_for_quickdraw (XFRAME (frame));
}
else
{
CGContextRef context = FRAME_CG_CONTEXT (f);
if (context)
{
CGContextSynchronize (context);
QDEndCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)),
&FRAME_CG_CONTEXT (f));
}
}
}
#endif
static RgnHandle saved_port_clip_region = NULL;
static void
mac_begin_clip (f, gc)
struct frame *f;
GC gc;
{
static RgnHandle new_region = NULL;
if (saved_port_clip_region == NULL)
saved_port_clip_region = NewRgn ();
if (new_region == NULL)
new_region = NewRgn ();
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
SetPortWindowPort (FRAME_MAC_WINDOW (f));
if (gc->n_clip_rects)
{
GetClip (saved_port_clip_region);
SectRgn (saved_port_clip_region, gc->clip_region, new_region);
SetClip (new_region);
}
}
static void
mac_end_clip (gc)
GC gc;
{
if (gc->n_clip_rects)
SetClip (saved_port_clip_region);
}
/* X display function emulation */
/* Mac version of XDrawLine. */
static void
mac_draw_line (f, gc, x1, y1, x2, y2)
struct frame *f;
GC gc;
int x1, y1, x2, y2;
{
#if USE_CG_DRAWING
CGContextRef context;
CGFloat gx1 = x1, gy1 = y1, gx2 = x2, gy2 = y2;
if (y1 != y2)
gx1 += 0.5f, gx2 += 0.5f;
if (x1 != x2)
gy1 += 0.5f, gy2 += 0.5f;
context = mac_begin_cg_clip (f, gc);
CG_SET_STROKE_COLOR_WITH_GC_FOREGROUND (context, gc);
CGContextBeginPath (context);
CGContextMoveToPoint (context, gx1, gy1);
CGContextAddLineToPoint (context, gx2, gy2);
CGContextClosePath (context);
CGContextStrokePath (context);
mac_end_cg_clip (f);
#else
if (x1 == x2)
{
if (y1 > y2)
y1--;
else if (y2 > y1)
y2--;
}
else if (y1 == y2)
{
if (x1 > x2)
x1--;
else
x2--;
}
mac_begin_clip (f, gc);
RGBForeColor (GC_FORE_COLOR (gc));
MoveTo (x1, y1);
LineTo (x2, y2);
mac_end_clip (gc);
#endif
}
/* Mac version of XDrawLine (to Pixmap). */
void
XDrawLine (display, p, gc, x1, y1, x2, y2)
Display *display;
Pixmap p;
GC gc;
int x1, y1, x2, y2;
{
#if USE_MAC_IMAGE_IO
CGContextRef context;
XImagePtr ximg = p;
CGColorSpaceRef color_space;
CGImageAlphaInfo alpha_info;
CGFloat gx1 = x1, gy1 = y1, gx2 = x2, gy2 = y2;
if (y1 != y2)
gx1 += 0.5f, gx2 += 0.5f;
if (x1 != x2)
gy1 += 0.5f, gy2 += 0.5f;
if (ximg->bits_per_pixel == 32)
{
color_space = mac_cg_color_space_rgb;
alpha_info = (kCGImageAlphaNoneSkipFirst
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
| kCGBitmapByteOrder32Host
#endif
);
}
else
{
color_space = NULL;
alpha_info = kCGImageAlphaOnly;
}
if (color_space == NULL)
return;
context = CGBitmapContextCreate (ximg->data, ximg->width,
ximg->height, 8,
ximg->bytes_per_line, color_space,
alpha_info);
if (ximg->bits_per_pixel == 32)
CG_SET_STROKE_COLOR_WITH_GC_FOREGROUND (context, gc);
else
CGContextSetGrayStrokeColor (context, gc->xgcv.foreground / 255.0f, 1.0);
CGContextMoveToPoint (context, gx1, gy1);
CGContextAddLineToPoint (context, gx2, gy2);
CGContextClosePath (context);
CGContextStrokePath (context);
CGContextRelease (context);
#else
CGrafPtr old_port;
GDHandle old_gdh;
if (x1 == x2)
{
if (y1 > y2)
y1--;
else if (y2 > y1)
y2--;
}
else if (y1 == y2)
{
if (x1 > x2)
x1--;
else
x2--;
}
GetGWorld (&old_port, &old_gdh);
SetGWorld (p, NULL);
RGBForeColor (GC_FORE_COLOR (gc));
LockPixels (GetGWorldPixMap (p));
MoveTo (x1, y1);
LineTo (x2, y2);
UnlockPixels (GetGWorldPixMap (p));
SetGWorld (old_port, old_gdh);
#endif
}
static void
mac_erase_rectangle (f, gc, x, y, width, height)
struct frame *f;
GC gc;
int x, y;
unsigned int width, height;
{
#if USE_CG_DRAWING
{
CGContextRef context;
context = mac_begin_cg_clip (f, gc);
CG_SET_FILL_COLOR_WITH_GC_BACKGROUND (context, gc);
CGContextFillRect (context, mac_rect_make (f, x, y, width, height));
mac_end_cg_clip (f);
}
#else
{
Rect r;
mac_begin_clip (f, gc);
RGBBackColor (GC_BACK_COLOR (gc));
SetRect (&r, x, y, x + width, y + height);
EraseRect (&r);
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
mac_end_clip (gc);
}
#endif
}
/* Mac version of XClearArea. */
void
mac_clear_area (f, x, y, width, height)
struct frame *f;
int x, y;
unsigned int width, height;
{
mac_erase_rectangle (f, FRAME_NORMAL_GC (f), x, y, width, height);
}
/* Mac version of XClearWindow. */
static void
mac_clear_window (f)
struct frame *f;
{
#if USE_CG_DRAWING
{
CGContextRef context;
GC gc = FRAME_NORMAL_GC (f);
context = mac_begin_cg_clip (f, NULL);
CG_SET_FILL_COLOR_WITH_GC_BACKGROUND (context, gc);
CGContextFillRect (context, CGRectMake (0, 0, FRAME_PIXEL_WIDTH (f),
FRAME_PIXEL_HEIGHT (f)));
mac_end_cg_clip (f);
}
#else /* !USE_CG_DRAWING */
SetPortWindowPort (FRAME_MAC_WINDOW (f));
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
#if TARGET_API_MAC_CARBON
{
Rect r;
GetWindowPortBounds (FRAME_MAC_WINDOW (f), &r);
EraseRect (&r);
}
#else /* not TARGET_API_MAC_CARBON */
EraseRect (&(FRAME_MAC_WINDOW (f)->portRect));
#endif /* not TARGET_API_MAC_CARBON */
#endif
}
/* Mac replacement for XCopyArea. */
#if USE_CG_DRAWING
static void
mac_draw_cg_image (image, f, gc, src_x, src_y, width, height,
dest_x, dest_y, overlay_p)
CGImageRef image;
struct frame *f;
GC gc;
int src_x, src_y;
unsigned int width, height;
int dest_x, dest_y, overlay_p;
{
CGContextRef context;
CGFloat port_height = FRAME_PIXEL_HEIGHT (f);
CGRect dest_rect = mac_rect_make (f, dest_x, dest_y, width, height);
context = mac_begin_cg_clip (f, gc);
if (!overlay_p)
{
CG_SET_FILL_COLOR_WITH_GC_BACKGROUND (context, gc);
CGContextFillRect (context, dest_rect);
}
CGContextClipToRect (context, dest_rect);
CGContextScaleCTM (context, 1, -1);
CGContextTranslateCTM (context, 0, -port_height);
if (CGImageIsMask (image))
CG_SET_FILL_COLOR_WITH_GC_FOREGROUND (context, gc);
CGContextDrawImage (context,
mac_rect_make (f, dest_x - src_x,
port_height - (dest_y - src_y
+ CGImageGetHeight (image)),
CGImageGetWidth (image),
CGImageGetHeight (image)),
image);
mac_end_cg_clip (f);
}
#else /* !USE_CG_DRAWING */
static void
mac_draw_bitmap (f, gc, x, y, width, height, bits, overlay_p)
struct frame *f;
GC gc;
int x, y, width, height;
unsigned short *bits;
int overlay_p;
{
BitMap bitmap;
Rect r;
bitmap.rowBytes = sizeof(unsigned short);
bitmap.baseAddr = (char *)bits;
SetRect (&(bitmap.bounds), 0, 0, width, height);
mac_begin_clip (f, gc);
RGBForeColor (GC_FORE_COLOR (gc));
RGBBackColor (GC_BACK_COLOR (gc));
SetRect (&r, x, y, x + width, y + height);
#if TARGET_API_MAC_CARBON
{
CGrafPtr port;
GetPort (&port);
LockPortBits (port);
CopyBits (&bitmap, GetPortBitMapForCopyBits (port),
&(bitmap.bounds), &r, overlay_p ? srcOr : srcCopy, 0);
UnlockPortBits (port);
}
#else /* not TARGET_API_MAC_CARBON */
CopyBits (&bitmap, &(FRAME_MAC_WINDOW (f)->portBits), &(bitmap.bounds), &r,
overlay_p ? srcOr : srcCopy, 0);
#endif /* not TARGET_API_MAC_CARBON */
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
mac_end_clip (gc);
}
#endif /* !USE_CG_DRAWING */
/* Mac replacement for XCreateBitmapFromBitmapData. */
static void
mac_create_bitmap_from_bitmap_data (bitmap, bits, w, h)
BitMap *bitmap;
char *bits;
int w, h;
{
static const unsigned char swap_nibble[16]
= { 0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
0x1, 0x9, 0x5, 0xd, /* 0001 1001 0101 1101 */
0x3, 0xb, 0x7, 0xf }; /* 0011 1011 0111 1111 */
int i, j, w1;
char *p;
w1 = (w + 7) / 8; /* nb of 8bits elt in X bitmap */
bitmap->rowBytes = ((w + 15) / 16) * 2; /* nb of 16bits elt in Mac bitmap */
bitmap->baseAddr = xmalloc (bitmap->rowBytes * h);
bzero (bitmap->baseAddr, bitmap->rowBytes * h);
for (i = 0; i < h; i++)
{
p = bitmap->baseAddr + i * bitmap->rowBytes;
for (j = 0; j < w1; j++)
{
/* Bitswap XBM bytes to match how Mac does things. */
unsigned char c = *bits++;
*p++ = (unsigned char)((swap_nibble[c & 0xf] << 4)
| (swap_nibble[(c>>4) & 0xf]));
}
}
SetRect (&(bitmap->bounds), 0, 0, w, h);
}
static void
mac_free_bitmap (bitmap)
BitMap *bitmap;
{
xfree (bitmap->baseAddr);
}
Pixmap
XCreatePixmap (display, w, width, height, depth)
Display *display;
Window w;
unsigned int width, height;
unsigned int depth;
{
#if USE_MAC_IMAGE_IO
XImagePtr ximg;
ximg = xmalloc (sizeof (*ximg));
ximg->width = width;
ximg->height = height;
ximg->bits_per_pixel = depth == 1 ? 8 : 32;
ximg->bytes_per_line = width * (ximg->bits_per_pixel / 8);
ximg->data = xmalloc (ximg->bytes_per_line * height);
return ximg;
#else
Pixmap pixmap;
Rect r;
QDErr err;
#ifdef MAC_OS8
SetPortWindowPort (w);
#endif
SetRect (&r, 0, 0, width, height);
#if !defined (WORDS_BIG_ENDIAN) && USE_CG_DRAWING
if (depth == 1)
#endif
err = NewGWorld (&pixmap, depth, &r, NULL, NULL, 0);
#if !defined (WORDS_BIG_ENDIAN) && USE_CG_DRAWING
else
/* CreateCGImageFromPixMaps requires ARGB format. */
err = QTNewGWorld (&pixmap, k32ARGBPixelFormat, &r, NULL, NULL, 0);
#endif
if (err != noErr)
return NULL;
return pixmap;
#endif
}
Pixmap
XCreatePixmapFromBitmapData (display, w, data, width, height, fg, bg, depth)
Display *display;
Window w;
char *data;
unsigned int width, height;
unsigned long fg, bg;
unsigned int depth;
{
Pixmap pixmap;
BitMap bitmap;
#if USE_MAC_IMAGE_IO
CGDataProviderRef provider;
CGImageRef image_mask;
CGContextRef context;
pixmap = XCreatePixmap (display, w, width, height, depth);
if (pixmap == NULL)
return NULL;
mac_create_bitmap_from_bitmap_data (&bitmap, data, width, height);
provider = CGDataProviderCreateWithData (NULL, bitmap.baseAddr,
bitmap.rowBytes * height, NULL);
image_mask = CGImageMaskCreate (width, height, 1, 1, bitmap.rowBytes,
provider, NULL, 0);
CGDataProviderRelease (provider);
context = CGBitmapContextCreate (pixmap->data, width, height, 8,
pixmap->bytes_per_line,
mac_cg_color_space_rgb,
kCGImageAlphaNoneSkipFirst
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
| kCGBitmapByteOrder32Host
#endif
);
CG_SET_FILL_COLOR (context, fg);
CGContextFillRect (context, CGRectMake (0, 0, width, height));
CG_SET_FILL_COLOR (context, bg);
CGContextDrawImage (context, CGRectMake (0, 0, width, height), image_mask);
CGContextRelease (context);
CGImageRelease (image_mask);
#else
CGrafPtr old_port;
GDHandle old_gdh;
static GC gc = NULL;
if (gc == NULL)
gc = XCreateGC (display, w, 0, NULL);
pixmap = XCreatePixmap (display, w, width, height, depth);
if (pixmap == NULL)
return NULL;
GetGWorld (&old_port, &old_gdh);
SetGWorld (pixmap, NULL);
mac_create_bitmap_from_bitmap_data (&bitmap, data, width, height);
XSetForeground (display, gc, fg);
XSetBackground (display, gc, bg);
RGBForeColor (GC_FORE_COLOR (gc));
RGBBackColor (GC_BACK_COLOR (gc));
LockPixels (GetGWorldPixMap (pixmap));
#if TARGET_API_MAC_CARBON
CopyBits (&bitmap, GetPortBitMapForCopyBits (pixmap),
&bitmap.bounds, &bitmap.bounds, srcCopy, 0);
#else /* not TARGET_API_MAC_CARBON */
CopyBits (&bitmap, &(((GrafPtr)pixmap)->portBits),
&bitmap.bounds, &bitmap.bounds, srcCopy, 0);
#endif /* not TARGET_API_MAC_CARBON */
UnlockPixels (GetGWorldPixMap (pixmap));
SetGWorld (old_port, old_gdh);
#endif
mac_free_bitmap (&bitmap);
return pixmap;
}
void
XFreePixmap (display, pixmap)
Display *display;
Pixmap pixmap;
{
#if USE_MAC_IMAGE_IO
if (pixmap)
{
if (pixmap->data)
xfree (pixmap->data);
xfree (pixmap);
}
#else
DisposeGWorld (pixmap);
#endif
}
/* Mac replacement for XFillRectangle. */
static void
mac_fill_rectangle (f, gc, x, y, width, height)
struct frame *f;
GC gc;
int x, y;
unsigned int width, height;
{
#if USE_CG_DRAWING
CGContextRef context;
context = mac_begin_cg_clip (f, gc);
CG_SET_FILL_COLOR_WITH_GC_FOREGROUND (context, gc);
CGContextFillRect (context, mac_rect_make (f, x, y, width, height));
mac_end_cg_clip (f);
#else
Rect r;
mac_begin_clip (f, gc);
RGBForeColor (GC_FORE_COLOR (gc));
SetRect (&r, x, y, x + width, y + height);
PaintRect (&r); /* using foreground color of gc */
mac_end_clip (gc);
#endif
}
/* Mac replacement for XDrawRectangle: dest is a window. */
static void
mac_draw_rectangle (f, gc, x, y, width, height)
struct frame *f;
GC gc;
int x, y;
unsigned int width, height;
{
#if USE_CG_DRAWING
CGContextRef context;
context = mac_begin_cg_clip (f, gc);
CG_SET_STROKE_COLOR_WITH_GC_FOREGROUND (context, gc);
CGContextStrokeRect (context,
CGRectMake (x + 0.5f, y + 0.5f, width, height));
mac_end_cg_clip (f);
#else
Rect r;
mac_begin_clip (f, gc);
RGBForeColor (GC_FORE_COLOR (gc));
SetRect (&r, x, y, x + width + 1, y + height + 1);
FrameRect (&r); /* using foreground color of gc */
mac_end_clip (gc);
#endif
}
static void
mac_invert_rectangle (f, x, y, width, height)
struct frame *f;
int x, y;
unsigned int width, height;
{
#if USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1040 && MAC_OS_X_VERSION_MIN_REQUIRED >= 1020
if (CGContextSetBlendMode != NULL)
#endif
{
CGContextRef context;
context = mac_begin_cg_clip (f, NULL);
CGContextSetRGBFillColor (context, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextSetBlendMode (context, kCGBlendModeDifference);
CGContextFillRect (context, mac_rect_make (f, x, y, width, height));
mac_end_cg_clip (f);
}
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1040 && MAC_OS_X_VERSION_MIN_REQUIRED >= 1020
else /* CGContextSetBlendMode == NULL */
#endif
#endif /* USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 */
#if !USE_CG_DRAWING || MAC_OS_X_VERSION_MAX_ALLOWED < 1040 || (MAC_OS_X_VERSION_MIN_REQUIRED < 1040 && MAC_OS_X_VERSION_MIN_REQUIRED >= 1020)
{
Rect r;
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
SetPortWindowPort (FRAME_MAC_WINDOW (f));
SetRect (&r, x, y, x + width, y + height);
InvertRect (&r);
}
#if USE_ATSUI
static OSStatus
atsu_get_text_layout_with_text_ptr (text, text_length, style, text_layout)
ConstUniCharArrayPtr text;
UniCharCount text_length;
ATSUStyle style;
ATSUTextLayout *text_layout;
{
OSStatus err;
static ATSUTextLayout saved_text_layout = NULL;
if (saved_text_layout == NULL)
{
static const UniCharCount lengths[] = {kATSUToTextEnd};
static const ATSUAttributeTag tags[] = {kATSULineLayoutOptionsTag};
static const ByteCount sizes[] = {sizeof (ATSLineLayoutOptions)};
static ATSLineLayoutOptions line_layout =
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
kATSLineDisableAllLayoutOperations | kATSLineUseDeviceMetrics
| kATSLineUseQDRendering
#else
kATSLineIsDisplayOnly | kATSLineFractDisable
#endif
;
static const ATSUAttributeValuePtr values[] = {&line_layout};
err = ATSUCreateTextLayoutWithTextPtr (text,
kATSUFromTextBeginning,
kATSUToTextEnd,
text_length,
1, lengths, &style,
&saved_text_layout);
if (err == noErr)
err = ATSUSetLayoutControls (saved_text_layout,
sizeof (tags) / sizeof (tags[0]),
tags, sizes, values);
if (err == noErr)
err = ATSUSetTransientFontMatching (saved_text_layout, true);
}
else
{
err = ATSUSetRunStyle (saved_text_layout, style,
kATSUFromTextBeginning, kATSUToTextEnd);
if (err == noErr)
err = ATSUSetTextPointerLocation (saved_text_layout, text,
kATSUFromTextBeginning,
kATSUToTextEnd,
text_length);
}
if (err == noErr)
*text_layout = saved_text_layout;
return err;
}
static void
mac_draw_image_string_atsui (f, gc, x, y, buf, nchars, bg_width,
overstrike_p, bytes_per_char)
struct frame *f;
GC gc;
int x, y;
char *buf;
int nchars, bg_width, overstrike_p, bytes_per_char;
{
OSStatus err;
ATSUTextLayout text_layout;
xassert (bytes_per_char == 2);
#ifndef WORDS_BIG_ENDIAN
{
int i;
UniChar *text = (UniChar *)buf;
for (i = 0; i < nchars; i++)
text[i] = EndianU16_BtoN (text[i]);
}
#endif
err = atsu_get_text_layout_with_text_ptr ((ConstUniCharArrayPtr)buf,
nchars,
GC_FONT (gc)->mac_style,
&text_layout);
if (err != noErr)
return;
#ifdef MAC_OSX
if (!mac_use_core_graphics)
{
#endif
mac_begin_clip (f, gc);
RGBForeColor (GC_FORE_COLOR (gc));
if (bg_width)
{
Rect r;
SetRect (&r, x, y - FONT_BASE (GC_FONT (gc)),
x + bg_width, y + FONT_DESCENT (GC_FONT (gc)));
RGBBackColor (GC_BACK_COLOR (gc));
EraseRect (&r);
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
}
MoveTo (x, y);
ATSUDrawText (text_layout,
kATSUFromTextBeginning, kATSUToTextEnd,
kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
if (overstrike_p)
{
MoveTo (x + 1, y);
ATSUDrawText (text_layout,
kATSUFromTextBeginning, kATSUToTextEnd,
kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
}
mac_end_clip (gc);
#ifdef MAC_OSX
}
else
{
static CGContextRef context;
CGFloat port_height = FRAME_PIXEL_HEIGHT (f);
static const ATSUAttributeTag tags[] = {kATSUCGContextTag};
static const ByteCount sizes[] = {sizeof (CGContextRef)};
static const ATSUAttributeValuePtr values[] = {&context};
#if USE_CG_DRAWING
context = mac_begin_cg_clip (f, gc);
#else
CGrafPtr port;
GetPort (&port);
QDBeginCGContext (port, &context);
if (gc->n_clip_rects || bg_width)
{
CGContextTranslateCTM (context, 0, port_height);
CGContextScaleCTM (context, 1, -1);
if (gc->n_clip_rects)
CGContextClipToRects (context, gc->clip_rects,
gc->n_clip_rects);
#endif
if (bg_width)
{
CG_SET_FILL_COLOR_WITH_GC_BACKGROUND (context, gc);
CGContextFillRect (context,
mac_rect_make (f,
x, y - FONT_BASE (GC_FONT (gc)),
bg_width,
FONT_HEIGHT (GC_FONT (gc))));
}
CGContextScaleCTM (context, 1, -1);
CGContextTranslateCTM (context, 0, -port_height);
#if !USE_CG_DRAWING
}
#endif
CG_SET_FILL_COLOR_WITH_GC_FOREGROUND (context, gc);
err = ATSUSetLayoutControls (text_layout,
sizeof (tags) / sizeof (tags[0]),
tags, sizes, values);
if (err == noErr)
{
ATSUDrawText (text_layout,
kATSUFromTextBeginning, kATSUToTextEnd,
Long2Fix (x), Long2Fix (port_height - y));
if (overstrike_p)
ATSUDrawText (text_layout,
kATSUFromTextBeginning, kATSUToTextEnd,
Long2Fix (x + 1), Long2Fix (port_height - y));
}
#if USE_CG_DRAWING
mac_end_cg_clip (f);
context = NULL;
#else
CGContextSynchronize (context);
QDEndCGContext (port, &context);
#endif
#if 0
/* This doesn't work on Mac OS X 10.1. */
ATSUClearLayoutControls (text_layout,
sizeof (tags) / sizeof (tags[0]), tags);
#else
ATSUSetLayoutControls (text_layout,
sizeof (tags) / sizeof (tags[0]),
tags, sizes, values);
#endif
}
#endif /* MAC_OSX */
}
#endif /* USE_ATSUI */
static void
mac_draw_image_string_qd (f, gc, x, y, buf, nchars, bg_width,
overstrike_p, bytes_per_char)
struct frame *f;
GC gc;
int x, y;
char *buf;
int nchars, bg_width, overstrike_p, bytes_per_char;
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
UInt32 savedFlags;
#endif
mac_begin_clip (f, gc);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
if (mac_use_core_graphics)
savedFlags = SwapQDTextFlags (kQDUseCGTextRendering);
#endif
RGBForeColor (GC_FORE_COLOR (gc));
#ifdef MAC_OS8
if (bg_width)
{
RGBBackColor (GC_BACK_COLOR (gc));
TextMode (srcCopy);
}
else
TextMode (srcOr);
#else
/* We prefer not to use srcCopy text transfer mode on Mac OS X
because:
- Screen is double-buffered. (In srcCopy mode, a text is drawn
into an offscreen graphics world first. So performance gain
cannot be expected.)
- It lowers rendering quality.
- Some fonts leave garbage on cursor movement. */
if (bg_width)
{
Rect r;
RGBBackColor (GC_BACK_COLOR (gc));
SetRect (&r, x, y - FONT_BASE (GC_FONT (gc)),
x + bg_width, y + FONT_DESCENT (GC_FONT (gc)));
EraseRect (&r);
}
TextMode (srcOr);
#endif
TextFont (GC_FONT (gc)->mac_fontnum);
TextSize (GC_FONT (gc)->mac_fontsize);
TextFace (GC_FONT (gc)->mac_fontface);
MoveTo (x, y);
DrawText (buf, 0, nchars * bytes_per_char);
if (overstrike_p)
{
TextMode (srcOr);
MoveTo (x + 1, y);
DrawText (buf, 0, nchars * bytes_per_char);
}
if (bg_width)
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
mac_end_clip (gc);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
if (mac_use_core_graphics)
SwapQDTextFlags(savedFlags);
#endif
}
static INLINE void
mac_draw_string_common (f, gc, x, y, buf, nchars, bg_width,
overstrike_p, bytes_per_char)
struct frame *f;
GC gc;
int x, y;
char *buf;
int nchars, bg_width, overstrike_p, bytes_per_char;
{
#if USE_ATSUI
if (GC_FONT (gc)->mac_style)
mac_draw_image_string_atsui (f, gc, x, y, buf, nchars, bg_width,
overstrike_p, bytes_per_char);
else
#endif /* USE_ATSUI */
mac_draw_image_string_qd (f, gc, x, y, buf, nchars, bg_width,
overstrike_p, bytes_per_char);
}
/* Mac replacement for XDrawImageString. */
static void
mac_draw_image_string (f, gc, x, y, buf, nchars, bg_width, overstrike_p)
struct frame *f;
GC gc;
int x, y;
char *buf;
int nchars, bg_width, overstrike_p;
{
mac_draw_string_common (f, gc, x, y, buf, nchars, bg_width,
overstrike_p, 1);
}
/* Mac replacement for XDrawImageString16. */
static void
mac_draw_image_string_16 (f, gc, x, y, buf, nchars, bg_width, overstrike_p)
struct frame *f;
GC gc;
int x, y;
XChar2b *buf;
int nchars, bg_width, overstrike_p;
{
mac_draw_string_common (f, gc, x, y, (char *) buf, nchars, bg_width,
overstrike_p, 2);
}
/* Mac replacement for XQueryTextExtents, but takes a character. If
STYLE is NULL, measurement is done by QuickDraw Text routines for
the font of the current graphics port. If CG_GLYPH is not NULL,
*CG_GLYPH is set to the glyph ID or 0 if it cannot be obtained. */
static OSStatus
mac_query_char_extents (style, c,
font_ascent_return, font_descent_return,
overall_return, cg_glyph)
#if USE_ATSUI
ATSUStyle style;
#else
void *style;
#endif
int c;
int *font_ascent_return, *font_descent_return;
XCharStruct *overall_return;
#if USE_CG_TEXT_DRAWING
CGGlyph *cg_glyph;
#else
void *cg_glyph;
#endif
{
OSStatus err = noErr;
int width;
Rect char_bounds;
#if USE_ATSUI
if (style)
{
ATSUTextLayout text_layout;
UniChar ch = c;
err = atsu_get_text_layout_with_text_ptr (&ch, 1, style, &text_layout);
if (err == noErr
&& (font_ascent_return || font_descent_return || overall_return))
{
ATSTrapezoid glyph_bounds;
err = ATSUGetGlyphBounds (text_layout, 0, 0,
kATSUFromTextBeginning, kATSUToTextEnd,
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
kATSUseFractionalOrigins,
#else
kATSUseDeviceOrigins,
#endif
1, &glyph_bounds, NULL);
if (err == noErr)
{
xassert (glyph_bounds.lowerRight.x - glyph_bounds.lowerLeft.x
== glyph_bounds.upperRight.x - glyph_bounds.upperLeft.x);
width = Fix2Long (glyph_bounds.upperRight.x
- glyph_bounds.upperLeft.x);
if (font_ascent_return)
*font_ascent_return = -Fix2Long (glyph_bounds.upperLeft.y);
if (font_descent_return)
*font_descent_return = Fix2Long (glyph_bounds.lowerLeft.y);
}
}
if (err == noErr && overall_return)
{
err = ATSUMeasureTextImage (text_layout,
kATSUFromTextBeginning, kATSUToTextEnd,
0, 0, &char_bounds);
if (err == noErr)
STORE_XCHARSTRUCT (*overall_return, width, char_bounds);
#if USE_CG_TEXT_DRAWING
if (err == noErr && cg_glyph)
{
OSStatus err1;
ATSUGlyphInfoArray glyph_info_array;
ByteCount count = sizeof (ATSUGlyphInfoArray);
err1 = ATSUMatchFontsToText (text_layout, kATSUFromTextBeginning,
kATSUToTextEnd, NULL, NULL, NULL);
if (err1 == noErr)
err1 = ATSUGetGlyphInfo (text_layout, kATSUFromTextBeginning,
kATSUToTextEnd, &count,
&glyph_info_array);
if (err1 == noErr
/* Make sure that we don't have to make layout
adjustments. */
&& glyph_info_array.glyphs[0].deltaY == 0.0f
&& glyph_info_array.glyphs[0].idealX == 0.0f
&& glyph_info_array.glyphs[0].screenX == 0)
{
xassert (glyph_info_array.glyphs[0].glyphID);
*cg_glyph = glyph_info_array.glyphs[0].glyphID;
}
else
*cg_glyph = 0;
}
#endif
}
}
else
#endif
{
if (font_ascent_return || font_descent_return)
{
FontInfo font_info;
GetFontInfo (&font_info);
if (font_ascent_return)
*font_ascent_return = font_info.ascent;
if (font_descent_return)
*font_descent_return = font_info.descent;
}
if (overall_return)
{
char ch = c;
width = CharWidth (ch);
QDTextBounds (1, &ch, &char_bounds);
STORE_XCHARSTRUCT (*overall_return, width, char_bounds);
}
}
return err;
}
/* Mac replacement for XTextExtents16. Only sets horizontal metrics. */
static int
mac_text_extents_16 (font_struct, string, nchars, overall_return)
XFontStruct *font_struct;
XChar2b *string;
int nchars;
XCharStruct *overall_return;
{
int i;
short width = 0, lbearing = 0, rbearing = 0;
XCharStruct *pcm;
for (i = 0; i < nchars; i++)
{
pcm = mac_per_char_metric (font_struct, string, 0);
if (pcm == NULL)
width += FONT_WIDTH (font_struct);
else
{
lbearing = min (lbearing, width + pcm->lbearing);
rbearing = max (rbearing, width + pcm->rbearing);
width += pcm->width;
}
string++;
}
overall_return->lbearing = lbearing;
overall_return->rbearing = rbearing;
overall_return->width = width;
/* What's the meaning of the return value of XTextExtents16? */
}
#if USE_CG_TEXT_DRAWING
static int cg_text_anti_aliasing_threshold = 8;
static void
init_cg_text_anti_aliasing_threshold ()
{
int threshold;
Boolean valid_p;
threshold =
CFPreferencesGetAppIntegerValue (CFSTR ("AppleAntiAliasingThreshold"),
kCFPreferencesCurrentApplication,
&valid_p);
if (valid_p)
cg_text_anti_aliasing_threshold = threshold;
}
static int
mac_draw_image_string_cg (f, gc, x, y, buf, nchars, bg_width, overstrike_p)
struct frame *f;
GC gc;
int x, y;
XChar2b *buf;
int nchars, bg_width, overstrike_p;
{
CGFloat port_height, gx, gy;
int i;
CGContextRef context;
CGGlyph *glyphs;
CGSize *advances;
if (!mac_use_core_graphics || GC_FONT (gc)->cg_font == NULL)
return 0;
port_height = FRAME_PIXEL_HEIGHT (f);
gx = x;
gy = port_height - y;
glyphs = (CGGlyph *)buf;
advances = alloca (sizeof (CGSize) * nchars);
if (advances == NULL)
return 0;
for (i = 0; i < nchars; i++)
{
XCharStruct *pcm = mac_per_char_metric (GC_FONT (gc), buf, 0);
advances[i].width = pcm->width;
advances[i].height = 0;
glyphs[i] = GC_FONT (gc)->cg_glyphs[buf->byte2];
buf++;
}
#if USE_CG_DRAWING
context = mac_begin_cg_clip (f, gc);
#else
QDBeginCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)), &context);
if (gc->n_clip_rects || bg_width)
{
CGContextTranslateCTM (context, 0, port_height);
CGContextScaleCTM (context, 1, -1);
if (gc->n_clip_rects)
CGContextClipToRects (context, gc->clip_rects, gc->n_clip_rects);
#endif
if (bg_width)
{
CG_SET_FILL_COLOR_WITH_GC_BACKGROUND (context, gc);
CGContextFillRect
(context,
mac_rect_make (f, gx, y - FONT_BASE (GC_FONT (gc)),
bg_width, FONT_HEIGHT (GC_FONT (gc))));
}
CGContextScaleCTM (context, 1, -1);
CGContextTranslateCTM (context, 0, -port_height);
#if !USE_CG_DRAWING
}
#endif
CG_SET_FILL_COLOR_WITH_GC_FOREGROUND (context, gc);
CGContextSetFont (context, GC_FONT (gc)->cg_font);
CGContextSetFontSize (context, GC_FONT (gc)->mac_fontsize);
if (GC_FONT (gc)->mac_fontsize <= cg_text_anti_aliasing_threshold)
CGContextSetShouldAntialias (context, false);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
if (CGContextShowGlyphsWithAdvances != NULL)
#endif
{
CGContextSetTextPosition (context, gx, gy);
CGContextShowGlyphsWithAdvances (context, glyphs, advances, nchars);
if (overstrike_p)
{
CGContextSetTextPosition (context, gx + 1.0f, gy);
CGContextShowGlyphsWithAdvances (context, glyphs, advances, nchars);
}
}
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
else /* CGContextShowGlyphsWithAdvances == NULL */
#endif
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1030 || MAC_OS_X_VERSION_MIN_REQUIRED == 1020
{
for (i = 0; i < nchars; i++)
{
CGContextShowGlyphsAtPoint (context, gx, gy, glyphs + i, 1);
if (overstrike_p)
CGContextShowGlyphsAtPoint (context, gx + 1.0f, gy, glyphs + i, 1);
gx += advances[i].width;
}
}
#endif
#if USE_CG_DRAWING
mac_end_cg_clip (f);
#else
CGContextSynchronize (context);
QDEndCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)), &context);
#endif
return 1;
}
#endif
#if !USE_CG_DRAWING
/* Mac replacement for XCopyArea: dest must be window. */
static void
mac_copy_area (src, f, gc, src_x, src_y, width, height, dest_x, dest_y)
Pixmap src;
struct frame *f;
GC gc;
int src_x, src_y;
unsigned int width, height;
int dest_x, dest_y;
{
Rect src_r, dest_r;
mac_begin_clip (f, gc);
SetRect (&src_r, src_x, src_y, src_x + width, src_y + height);
SetRect (&dest_r, dest_x, dest_y, dest_x + width, dest_y + height);
ForeColor (blackColor);
BackColor (whiteColor);
LockPixels (GetGWorldPixMap (src));
#if TARGET_API_MAC_CARBON
{
CGrafPtr port;
GetPort (&port);
LockPortBits (port);
CopyBits (GetPortBitMapForCopyBits (src),
GetPortBitMapForCopyBits (port),
&src_r, &dest_r, srcCopy, 0);
UnlockPortBits (port);
}
#else /* not TARGET_API_MAC_CARBON */
CopyBits (&(((GrafPtr)src)->portBits), &(FRAME_MAC_WINDOW (f)->portBits),
&src_r, &dest_r, srcCopy, 0);
#endif /* not TARGET_API_MAC_CARBON */
UnlockPixels (GetGWorldPixMap (src));
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
mac_end_clip (gc);
}
static void
mac_copy_area_with_mask (src, mask, f, gc, src_x, src_y,
width, height, dest_x, dest_y)
Pixmap src, mask;
struct frame *f;
GC gc;
int src_x, src_y;
unsigned int width, height;
int dest_x, dest_y;
{
Rect src_r, dest_r;
mac_begin_clip (f, gc);
SetRect (&src_r, src_x, src_y, src_x + width, src_y + height);
SetRect (&dest_r, dest_x, dest_y, dest_x + width, dest_y + height);
ForeColor (blackColor);
BackColor (whiteColor);
LockPixels (GetGWorldPixMap (src));
LockPixels (GetGWorldPixMap (mask));
#if TARGET_API_MAC_CARBON
{
CGrafPtr port;
GetPort (&port);
LockPortBits (port);
CopyMask (GetPortBitMapForCopyBits (src), GetPortBitMapForCopyBits (mask),
GetPortBitMapForCopyBits (port),
&src_r, &src_r, &dest_r);
UnlockPortBits (port);
}
#else /* not TARGET_API_MAC_CARBON */
CopyMask (&(((GrafPtr)src)->portBits), &(((GrafPtr)mask)->portBits),
&(FRAME_MAC_WINDOW (f)->portBits), &src_r, &src_r, &dest_r);
#endif /* not TARGET_API_MAC_CARBON */
UnlockPixels (GetGWorldPixMap (mask));
UnlockPixels (GetGWorldPixMap (src));
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
mac_end_clip (gc);
}
#endif /* !USE_CG_DRAWING */
/* Mac replacement for XCopyArea: used only for scrolling. */
static void
mac_scroll_area (f, gc, src_x, src_y, width, height, dest_x, dest_y)
struct frame *f;
GC gc;
int src_x, src_y;
unsigned int width, height;
int dest_x, dest_y;
{
#if TARGET_API_MAC_CARBON
Rect src_r;
RgnHandle dummy = NewRgn (); /* For avoiding update events. */
SetRect (&src_r, src_x, src_y, src_x + width, src_y + height);
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
ScrollWindowRect (FRAME_MAC_WINDOW (f),
&src_r, dest_x - src_x, dest_y - src_y,
kScrollWindowNoOptions, dummy);
DisposeRgn (dummy);
#else /* not TARGET_API_MAC_CARBON */
Rect src_r, dest_r;
WindowRef w = FRAME_MAC_WINDOW (f);
mac_begin_clip (f, gc);
SetRect (&src_r, src_x, src_y, src_x + width, src_y + height);
SetRect (&dest_r, dest_x, dest_y, dest_x + width, dest_y + height);
/* In Color QuickDraw, set ForeColor and BackColor as follows to avoid
color mapping in CopyBits. Otherwise, it will be slow. */
ForeColor (blackColor);
BackColor (whiteColor);
CopyBits (&(w->portBits), &(w->portBits), &src_r, &dest_r, srcCopy, 0);
RGBBackColor (GC_BACK_COLOR (FRAME_NORMAL_GC (f)));
mac_end_clip (gc);
#endif /* not TARGET_API_MAC_CARBON */
}
/* Mac replacement for XChangeGC. */
static void
XChangeGC (display, gc, mask, xgcv)
Display *display;
GC gc;
unsigned long mask;
XGCValues *xgcv;
{
if (mask & GCForeground)
XSetForeground (display, gc, xgcv->foreground);
if (mask & GCBackground)
XSetBackground (display, gc, xgcv->background);
if (mask & GCFont)
XSetFont (display, gc, xgcv->font);
}
/* Mac replacement for XCreateGC. */
GC
XCreateGC (display, d, mask, xgcv)
Display *display;
void *d;
unsigned long mask;
XGCValues *xgcv;
{
GC gc = xmalloc (sizeof (*gc));
bzero (gc, sizeof (*gc));
#if USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
if (CGColorGetTypeID != NULL)
#endif
{
gc->cg_fore_color = gc->cg_back_color = mac_cg_color_black;
CGColorRetain (gc->cg_fore_color);
CGColorRetain (gc->cg_back_color);
}
#endif
XChangeGC (display, gc, mask, xgcv);
return gc;
}
/* Used in xfaces.c. */
void
XFreeGC (display, gc)
Display *display;
GC gc;
{
if (gc->clip_region)
DisposeRgn (gc->clip_region);
#if USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
if (CGColorGetTypeID != NULL)
#endif
{
CGColorRelease (gc->cg_fore_color);
CGColorRelease (gc->cg_back_color);
}
#endif
xfree (gc);
}
/* Mac replacement for XGetGCValues. */
static void
XGetGCValues (display, gc, mask, xgcv)
Display *display;
GC gc;
unsigned long mask;
XGCValues *xgcv;
{
if (mask & GCForeground)
xgcv->foreground = gc->xgcv.foreground;
if (mask & GCBackground)
xgcv->background = gc->xgcv.background;
if (mask & GCFont)
xgcv->font = gc->xgcv.font;
}
/* Mac replacement for XSetForeground. */
void
XSetForeground (display, gc, color)
Display *display;
GC gc;
unsigned long color;
{
if (gc->xgcv.foreground != color)
{
gc->xgcv.foreground = color;
gc->fore_color.red = RED16_FROM_ULONG (color);
gc->fore_color.green = GREEN16_FROM_ULONG (color);
gc->fore_color.blue = BLUE16_FROM_ULONG (color);
#if USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
if (CGColorGetTypeID != NULL)
#endif
{
CGColorRelease (gc->cg_fore_color);
if (color == 0)
{
gc->cg_fore_color = mac_cg_color_black;
CGColorRetain (gc->cg_fore_color);
}
else
{
CGFloat rgba[4];
rgba[0] = gc->fore_color.red / 65535.0f;
rgba[1] = gc->fore_color.green / 65535.0f;
rgba[2] = gc->fore_color.blue / 65535.0f;
rgba[3] = 1.0f;
gc->cg_fore_color = CGColorCreate (mac_cg_color_space_rgb, rgba);
}
}
#endif
}
}
/* Mac replacement for XSetBackground. */
void
XSetBackground (display, gc, color)
Display *display;
GC gc;
unsigned long color;
{
if (gc->xgcv.background != color)
{
gc->xgcv.background = color;
gc->back_color.red = RED16_FROM_ULONG (color);
gc->back_color.green = GREEN16_FROM_ULONG (color);
gc->back_color.blue = BLUE16_FROM_ULONG (color);
#if USE_CG_DRAWING && MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
if (CGColorGetTypeID != NULL)
#endif
{
CGColorRelease (gc->cg_back_color);
if (color == 0)
{
gc->cg_back_color = mac_cg_color_black;
CGColorRetain (gc->cg_back_color);
}
else
{
CGFloat rgba[4];
rgba[0] = gc->back_color.red / 65535.0f;
rgba[1] = gc->back_color.green / 65535.0f;
rgba[2] = gc->back_color.blue / 65535.0f;
rgba[3] = 1.0f;
gc->cg_back_color = CGColorCreate (mac_cg_color_space_rgb, rgba);
}
}
#endif
}
}
/* Mac replacement for XSetFont. */
static void
XSetFont (display, gc, font)
Display *display;
GC gc;
XFontStruct *font;
{
gc->xgcv.font = font;
}
/* Mac replacement for XSetClipRectangles. */
static void
mac_set_clip_rectangles (f, gc, rectangles, n)
struct frame *f;
GC gc;
Rect *rectangles;
int n;
{
int i;
xassert (n >= 0 && n <= MAX_CLIP_RECTS);
gc->n_clip_rects = n;
if (n > 0)
{
if (gc->clip_region == NULL)
gc->clip_region = NewRgn ();
RectRgn (gc->clip_region, rectangles);
if (n > 1)
{
RgnHandle region = NewRgn ();
for (i = 1; i < n; i++)
{
RectRgn (region, rectangles + i);
UnionRgn (gc->clip_region, region, gc->clip_region);
}
DisposeRgn (region);
}
}
#if defined (MAC_OSX) && (USE_ATSUI || USE_CG_DRAWING)
for (i = 0; i < n; i++)
{
Rect *rect = rectangles + i;
gc->clip_rects[i] = mac_rect_make (f, rect->left, rect->top,
rect->right - rect->left,
rect->bottom - rect->top);
}
#endif
}
/* Mac replacement for XSetClipMask. */
static INLINE void
mac_reset_clip_rectangles (f, gc)
struct frame *f;
GC gc;
{
gc->n_clip_rects = 0;
}
/* Mac replacement for XSetWindowBackground. */
void
XSetWindowBackground (display, w, color)
Display *display;
WindowRef w;
unsigned long color;
{
#if !TARGET_API_MAC_CARBON
AuxWinHandle aw_handle;
CTabHandle ctab_handle;
ColorSpecPtr ct_table;
short ct_size;
#endif
RGBColor bg_color;
bg_color.red = RED16_FROM_ULONG (color);
bg_color.green = GREEN16_FROM_ULONG (color);
bg_color.blue = BLUE16_FROM_ULONG (color);
#if TARGET_API_MAC_CARBON
SetWindowContentColor (w, &bg_color);
#else
if (GetAuxWin (w, &aw_handle))
{
ctab_handle = (*aw_handle)->awCTable;
HandToHand ((Handle *) &ctab_handle);
ct_table = (*ctab_handle)->ctTable;
ct_size = (*ctab_handle)->ctSize;
while (ct_size > -1)
{
if (ct_table->value == 0)
{
ct_table->rgb = bg_color;
CTabChanged (ctab_handle);
SetWinColor (w, (WCTabHandle) ctab_handle);
}
ct_size--;
}
}
#endif
}
/* Flush display of frame F, or of all frames if F is null. */
static void
x_flush (f)
struct frame *f;
{
#if TARGET_API_MAC_CARBON
BLOCK_INPUT;
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
if (f)
QDFlushPortBuffer (GetWindowPort (FRAME_MAC_WINDOW (f)), NULL);
else
QDFlushPortBuffer (GetQDGlobalsThePort (), NULL);
UNBLOCK_INPUT;
#endif
}
/* Remove calls to XFlush by defining XFlush to an empty replacement.
Calls to XFlush should be unnecessary because the X output buffer
is flushed automatically as needed by calls to XPending,
XNextEvent, or XWindowEvent according to the XFlush man page.
XTread_socket calls XPending. Removing XFlush improves
performance. */
#define XFlush(DISPLAY) (void) 0
#if USE_CG_DRAWING
static void
mac_flush_display_optional (f)
struct frame *f;
{
BLOCK_INPUT;
mac_prepare_for_quickdraw (f);
UNBLOCK_INPUT;
}
#endif
/***********************************************************************
Starting and ending an update
***********************************************************************/
/* Start an update of frame F. This function is installed as a hook
for update_begin, i.e. it is called when update_begin is called.
This function is called prior to calls to x_update_window_begin for
each window being updated. */
static void
x_update_begin (f)
struct frame *f;
{
#if TARGET_API_MAC_CARBON
/* During update of a frame, availability of input events is
periodically checked with ReceiveNextEvent if
redisplay-dont-pause is nil. That normally flushes window buffer
changes for every check, and thus screen update looks waving even
if no input is available. So we disable screen updates during
update of a frame. */
BLOCK_INPUT;
DisableScreenUpdates ();
UNBLOCK_INPUT;
#endif
}
/* Start update of window W. Set the global variable updated_window
to the window being updated and set output_cursor to the cursor
position of W. */
static void
x_update_window_begin (w)
struct window *w;
{
struct frame *f = XFRAME (WINDOW_FRAME (w));
struct mac_display_info *display_info = FRAME_MAC_DISPLAY_INFO (f);
updated_window = w;
set_output_cursor (&w->cursor);
BLOCK_INPUT;
if (f == display_info->mouse_face_mouse_frame)
{
/* Don't do highlighting for mouse motion during the update. */
display_info->mouse_face_defer = 1;
/* If F needs to be redrawn, simply forget about any prior mouse
highlighting. */
if (FRAME_GARBAGED_P (f))
display_info->mouse_face_window = Qnil;
#if 0 /* Rows in a current matrix containing glyphs in mouse-face have
their mouse_face_p flag set, which means that they are always
unequal to rows in a desired matrix which never have that
flag set. So, rows containing mouse-face glyphs are never
scrolled, and we don't have to switch the mouse highlight off
here to prevent it from being scrolled. */
/* Can we tell that this update does not affect the window
where the mouse highlight is? If so, no need to turn off.
Likewise, don't do anything if the frame is garbaged;
in that case, the frame's current matrix that we would use
is all wrong, and we will redisplay that line anyway. */
if (!NILP (display_info->mouse_face_window)
&& w == XWINDOW (display_info->mouse_face_window))
{
int i;
for (i = 0; i < w->desired_matrix->nrows; ++i)
if (MATRIX_ROW_ENABLED_P (w->desired_matrix, i))
break;
if (i < w->desired_matrix->nrows)
clear_mouse_face (display_info);
}
#endif /* 0 */
}
UNBLOCK_INPUT;
}
/* Draw a vertical window border from (x,y0) to (x,y1) */
static void
mac_draw_vertical_window_border (w, x, y0, y1)
struct window *w;
int x, y0, y1;
{
struct frame *f = XFRAME (WINDOW_FRAME (w));
struct face *face;
face = FACE_FROM_ID (f, VERTICAL_BORDER_FACE_ID);
if (face)
XSetForeground (FRAME_MAC_DISPLAY (f), f->output_data.mac->normal_gc,
face->foreground);
mac_draw_line (f, f->output_data.mac->normal_gc, x, y0, x, y1);
}
/* End update of window W (which is equal to updated_window).
Draw vertical borders between horizontally adjacent windows, and
display W's cursor if CURSOR_ON_P is non-zero.
MOUSE_FACE_OVERWRITTEN_P non-zero means that some row containing
glyphs in mouse-face were overwritten. In that case we have to
make sure that the mouse-highlight is properly redrawn.
W may be a menu bar pseudo-window in case we don't have X toolkit
support. Such windows don't have a cursor, so don't display it
here. */
static void
x_update_window_end (w, cursor_on_p, mouse_face_overwritten_p)
struct window *w;
int cursor_on_p, mouse_face_overwritten_p;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (XFRAME (w->frame));
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
XTframe_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;
}
/* End update of frame F. This function is installed as a hook in
update_end. */
static void
x_update_end (f)
struct frame *f;
{
/* Mouse highlight may be displayed again. */
FRAME_MAC_DISPLAY_INFO (f)->mouse_face_defer = 0;
BLOCK_INPUT;
#if TARGET_API_MAC_CARBON
EnableScreenUpdates ();
#endif
XFlush (FRAME_MAC_DISPLAY (f));
UNBLOCK_INPUT;
}
/* This function is called from various places in xdisp.c whenever a
complete update has been performed. The global variable
updated_window is not available here. */
static void
XTframe_up_to_date (f)
struct frame *f;
{
if (FRAME_MAC_P (f))
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
if (dpyinfo->mouse_face_deferred_gc
|| f == 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;
}
}
}
/* Draw truncation mark bitmaps, continuation mark bitmaps, overlay
arrow bitmaps, or clear the fringes if no bitmaps are required
before DESIRED_ROW is made current. The window being updated is
found in updated_window. This function is called from
update_window_line only if it is known that there are differences
between bitmaps to be drawn between current row and DESIRED_ROW. */
static void
x_after_update_window_line (desired_row)
struct glyph_row *desired_row;
{
struct window *w = updated_window;
struct frame *f;
int width, height;
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. Could
check here if updated_window is the leftmost/rightmost window,
but I guess it's not worth doing since vertically split windows
are almost never used, internal border is rarely set, and the
overhead is very small. */
if (windows_or_buffers_changed
&& desired_row->full_width_p
&& (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;
BLOCK_INPUT;
mac_clear_area (f, 0, y, width, height);
mac_clear_area (f, FRAME_PIXEL_WIDTH (f) - width, y, width, height);
UNBLOCK_INPUT;
}
}
/* Draw the bitmap WHICH in one of the left or right fringes of
window W. ROW is the glyph row for which to display the bitmap; it
determines the vertical position at which the bitmap has to be
drawn. */
static void
x_draw_fringe_bitmap (w, row, p)
struct window *w;
struct glyph_row *row;
struct draw_fringe_bitmap_params *p;
{
struct frame *f = XFRAME (WINDOW_FRAME (w));
Display *display = FRAME_MAC_DISPLAY (f);
struct face *face = p->face;
int rowY;
int overlay_p = p->overlay_p;
#ifdef MAC_OSX
if (!overlay_p)
{
int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
#if 0 /* MAC_TODO: stipple */
/* In case the same realized face is used for fringes and
for something displayed in the text (e.g. face `region' on
mono-displays, the fill style may have been changed to
FillSolid in x_draw_glyph_string_background. */
if (face->stipple)
XSetFillStyle (FRAME_X_DISPLAY (f), face->gc, FillOpaqueStippled);
else
XSetForeground (FRAME_X_DISPLAY (f), face->gc, face->background);
#endif
/* If the fringe is adjacent to the left (right) scroll bar of a
leftmost (rightmost, respectively) window, then extend its
background to the gap between the fringe and the bar. */
if ((WINDOW_LEFTMOST_P (w)
&& WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (w))
|| (WINDOW_RIGHTMOST_P (w)
&& WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_RIGHT (w)))
{
int sb_width = WINDOW_CONFIG_SCROLL_BAR_WIDTH (w);
if (sb_width > 0)
{
int left = WINDOW_SCROLL_BAR_AREA_X (w);
int width = (WINDOW_CONFIG_SCROLL_BAR_COLS (w)
* FRAME_COLUMN_WIDTH (f));
if (bx < 0
&& (left + width == p->x
|| p->x + p->wd == left))
{
/* Bitmap fills the fringe and we need background
extension. */
int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w);
bx = p->x;
nx = p->wd;
by = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height,
row->y));
ny = row->visible_height;
}
if (bx >= 0)
{
if (left + width == bx)
{
bx = left + sb_width;
nx += width - sb_width;
}
else if (bx + nx == left)
nx += width - sb_width;
}
}
}
if (bx >= 0)
{
mac_erase_rectangle (f, face->gc, bx, by, nx, ny);
/* The fringe background has already been filled. */
overlay_p = 1;
}
#if 0 /* MAC_TODO: stipple */
if (!face->stipple)
XSetForeground (FRAME_X_DISPLAY (f), face->gc, face->foreground);
#endif
}
#endif /* MAC_OSX */
/* 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;
x_clip_to_row (w, row, -1, face->gc);
row->y = oldY;
row->visible_height = oldVH;
}
else
x_clip_to_row (w, row, -1, face->gc);
#ifndef MAC_OSX
if (p->bx >= 0 && !p->overlay_p)
{
#if 0 /* MAC_TODO: stipple */
/* In case the same realized face is used for fringes and
for something displayed in the text (e.g. face `region' on
mono-displays, the fill style may have been changed to
FillSolid in x_draw_glyph_string_background. */
if (face->stipple)
XSetFillStyle (FRAME_X_DISPLAY (f), face->gc, FillOpaqueStippled);
else
XSetForeground (FRAME_X_DISPLAY (f), face->gc, face->background);
#endif
mac_erase_rectangle (f, face->gc, p->bx, p->by, p->nx, p->ny);
#if 0 /* MAC_TODO: stipple */
if (!face->stipple)
XSetForeground (FRAME_X_DISPLAY (f), face->gc, face->foreground);
#endif
}
#endif /* !MAC_OSX */
if (p->which
#if USE_CG_DRAWING
&& p->which < max_fringe_bmp
#endif
)
{
XGCValues gcv;
XGetGCValues (display, face->gc, GCForeground, &gcv);
XSetForeground (display, face->gc,
(p->cursor_p
? (p->overlay_p ? face->background
: f->output_data.mac->cursor_pixel)
: face->foreground));
#if USE_CG_DRAWING
mac_draw_cg_image (fringe_bmp[p->which], f, face->gc, 0, p->dh,
p->wd, p->h, p->x, p->y, overlay_p);
#else
mac_draw_bitmap (f, face->gc, p->x, p->y,
p->wd, p->h, p->bits + p->dh, overlay_p);
#endif
XSetForeground (display, face->gc, gcv.foreground);
}
mac_reset_clip_rectangles (f, face->gc);
}
#if USE_CG_DRAWING
static void
mac_define_fringe_bitmap (which, bits, h, wd)
int which;
unsigned short *bits;
int h, wd;
{
int i;
CGDataProviderRef provider;
if (which >= max_fringe_bmp)
{
i = max_fringe_bmp;
max_fringe_bmp = which + 20;
fringe_bmp = (CGImageRef *) xrealloc (fringe_bmp, max_fringe_bmp * sizeof (CGImageRef));
while (i < max_fringe_bmp)
fringe_bmp[i++] = 0;
}
for (i = 0; i < h; i++)
bits[i] = ~bits[i];
BLOCK_INPUT;
provider = CGDataProviderCreateWithData (NULL, bits,
sizeof (unsigned short) * h, NULL);
if (provider)
{
fringe_bmp[which] = CGImageMaskCreate (wd, h, 1, 1,
sizeof (unsigned short),
provider, NULL, 0);
CGDataProviderRelease (provider);
}
UNBLOCK_INPUT;
}
static void
mac_destroy_fringe_bitmap (which)
int which;
{
if (which >= max_fringe_bmp)
return;
if (fringe_bmp[which])
{
BLOCK_INPUT;
CGImageRelease (fringe_bmp[which]);
UNBLOCK_INPUT;
}
fringe_bmp[which] = 0;
}
#endif
/* This is called when starting Emacs and when restarting after
suspend. When starting Emacs, no window is mapped. And nothing
must be done to Emacs's own window if it is suspended (though that
rarely happens). */
static void
XTset_terminal_modes (struct terminal *t)
{
}
/* This is called when exiting or suspending Emacs. Exiting will make
the windows go away, and suspending requires no action. */
static void
XTreset_terminal_modes (struct terminal *t)
{
}
/***********************************************************************
Display Iterator
***********************************************************************/
/* Function prototypes of this page. */
static XCharStruct *x_per_char_metric P_ ((XFontStruct *, XChar2b *));
static int mac_encode_char P_ ((int, XChar2b *, struct font_info *,
struct charset *, int *));
static void
pcm_init (pcm, count)
XCharStruct *pcm;
int count;
{
bzero (pcm, sizeof (XCharStruct) * count);
while (--count >= 0)
{
pcm->descent = PCM_INVALID;
pcm++;
}
}
static enum pcm_status
pcm_get_status (pcm)
const XCharStruct *pcm;
{
int height = pcm->ascent + pcm->descent;
/* Negative height means some special status. */
return height >= 0 ? PCM_VALID : height;
}
/* Get metrics of character CHAR2B in FONT. Value is null if CHAR2B
is not contained in the font. */
static INLINE XCharStruct *
x_per_char_metric (font, char2b)
XFontStruct *font;
XChar2b *char2b;
{
/* The result metric information. */
XCharStruct *pcm = NULL;
xassert (font && char2b);
#if USE_ATSUI
if (font->mac_style)
{
XCharStruct **row = font->bounds.rows + char2b->byte1;
if (*row == NULL)
{
*row = xmalloc (sizeof (XCharStruct) * 0x100);
pcm_init (*row, 0x100);
}
pcm = *row + char2b->byte2;
if (pcm_get_status (pcm) != PCM_VALID)
{
BLOCK_INPUT;
mac_query_char_extents (font->mac_style,
(char2b->byte1 << 8) + char2b->byte2,
NULL, NULL, pcm, NULL);
UNBLOCK_INPUT;
}
}
else
{
#endif
if (font->bounds.per_char != NULL)
{
if (font->min_byte1 == 0 && font->max_byte1 == 0)
{
/* min_char_or_byte2 specifies the linear character index
corresponding to the first element of the per_char array,
max_char_or_byte2 is the index of the last character. A
character with non-zero CHAR2B->byte1 is not in the font.
A character with byte2 less than min_char_or_byte2 or
greater max_char_or_byte2 is not in the font. */
if (char2b->byte1 == 0
&& char2b->byte2 >= font->min_char_or_byte2
&& char2b->byte2 <= font->max_char_or_byte2)
pcm = font->bounds.per_char
+ (char2b->byte2 - font->min_char_or_byte2);
}
else
{
/* If either min_byte1 or max_byte1 are nonzero, both
min_char_or_byte2 and max_char_or_byte2 are less than
256, and the 2-byte character index values corresponding
to the per_char array element N (counting from 0) are:
byte1 = N/D + min_byte1
byte2 = N\D + min_char_or_byte2
where:
D = max_char_or_byte2 - min_char_or_byte2 + 1
/ = integer division
\ = integer modulus */
if (char2b->byte1 >= font->min_byte1
&& char2b->byte1 <= font->max_byte1
&& char2b->byte2 >= font->min_char_or_byte2
&& char2b->byte2 <= font->max_char_or_byte2)
{
pcm = (font->bounds.per_char
+ ((font->max_char_or_byte2 - font->min_char_or_byte2 + 1)
* (char2b->byte1 - font->min_byte1))
+ (char2b->byte2 - font->min_char_or_byte2));
}
}
}
else
{
/* If the per_char pointer is null, all glyphs between the first
and last character indexes inclusive have the same
information, as given by both min_bounds and max_bounds. */
if (char2b->byte2 >= font->min_char_or_byte2
&& char2b->byte2 <= font->max_char_or_byte2)
pcm = &font->max_bounds;
}
#if USE_ATSUI
}
#endif
return ((pcm == NULL
|| (pcm->width == 0
#if 0 /* Show hollow boxes for zero-width glyphs such as combining diacritics. */
&& (pcm->rbearing - pcm->lbearing) == 0
#endif
))
? NULL : pcm);
}
/* RIF:
*/
static XCharStruct *
mac_per_char_metric (font, char2b, font_type)
XFontStruct *font;
XChar2b *char2b;
int font_type;
{
return x_per_char_metric (font, char2b);
}
/* RIF:
Encode CHAR2B using encoding information from FONT_INFO. CHAR2B is
the two-byte form of C. Encoding is returned in *CHAR2B. */
static int
mac_encode_char (c, char2b, font_info, charset, two_byte_p)
int c;
XChar2b *char2b;
struct font_info *font_info;
struct charset *charset;
int *two_byte_p;
{
XFontStruct *font = font_info->font;
/* FONT_INFO may define a scheme by which to encode byte1 and byte2.
This may be either a program in a special encoder language or a
fixed encoding. */
if (font_info->font_encoder)
{
/* It's a program. */
struct ccl_program *ccl = font_info->font_encoder;
check_ccl_update (ccl);
if (CHARSET_DIMENSION (charset) == 1)
{
ccl->reg[0] = CHARSET_ID (charset);
ccl->reg[1] = XCHAR2B_BYTE2 (char2b);
ccl->reg[2] = -1;
}
else
{
ccl->reg[0] = CHARSET_ID (charset);
ccl->reg[1] = XCHAR2B_BYTE1 (char2b);
ccl->reg[2] = XCHAR2B_BYTE2 (char2b);
}
ccl_driver (ccl, NULL, NULL, 0, 0, Qnil);
/* We assume that MSBs are appropriately set/reset by CCL
program. */
if (font->max_byte1 == 0) /* 1-byte font */
STORE_XCHAR2B (char2b, 0, ccl->reg[1]);
else
STORE_XCHAR2B (char2b, ccl->reg[1], ccl->reg[2]);
}
else if (font_info->encoding_type)
{
/* Fixed encoding scheme. See fontset.h for the meaning of the
encoding numbers. */
unsigned char enc = font_info->encoding_type;
if ((enc == 1 || enc == 2)
&& CHARSET_DIMENSION (charset) == 2)
char2b->byte1 |= 0x80;
if (enc == 1 || enc == 3)
char2b->byte2 |= 0x80;
if (enc == 4)
{
int code = (char2b->byte1 << 8) | char2b->byte2;
JIS_TO_SJIS (code);
STORE_XCHAR2B (char2b, (code >> 8), (code & 0xFF));
}
}
if (two_byte_p)
*two_byte_p = ((XFontStruct *) (font_info->font))->max_byte1 > 0;
return FONT_TYPE_UNKNOWN;
}
/***********************************************************************
Glyph display
***********************************************************************/
static void x_set_glyph_string_clipping P_ ((struct glyph_string *));
static void x_set_glyph_string_gc P_ ((struct glyph_string *));
static void x_draw_glyph_string_background P_ ((struct glyph_string *,
int));
static void x_draw_glyph_string_foreground P_ ((struct glyph_string *));
static void x_draw_composite_glyph_string_foreground P_ ((struct glyph_string *));
static void x_draw_glyph_string_box P_ ((struct glyph_string *));
static void x_draw_glyph_string P_ ((struct glyph_string *));
static void mac_compute_glyph_string_overhangs P_ ((struct glyph_string *));
static void x_set_cursor_gc P_ ((struct glyph_string *));
static void x_set_mode_line_face_gc P_ ((struct glyph_string *));
static void x_set_mouse_face_gc P_ ((struct glyph_string *));
/*static int x_alloc_lighter_color P_ ((struct frame *, Display *, Colormap,
unsigned long *, double, int));*/
static void x_setup_relief_color P_ ((struct frame *, struct relief *,
double, int, unsigned long));
static void x_setup_relief_colors P_ ((struct glyph_string *));
static void x_draw_image_glyph_string P_ ((struct glyph_string *));
static void x_draw_image_relief P_ ((struct glyph_string *));
static void x_draw_image_foreground P_ ((struct glyph_string *));
static void x_clear_glyph_string_rect P_ ((struct glyph_string *, int,
int, int, int));
static void x_draw_relief_rect P_ ((struct frame *, int, int, int, int,
int, int, int, int, int, int,
Rect *));
static void x_draw_box_rect P_ ((struct glyph_string *, int, int, int, int,
int, int, int, Rect *));
#if GLYPH_DEBUG
static void x_check_font P_ ((struct frame *, XFontStruct *));
#endif
/* Set S->gc to a suitable GC for drawing glyph string S in cursor
face. */
static void
x_set_cursor_gc (s)
struct glyph_string *s;
{
if (s->font == FRAME_FONT (s->f)
&& s->face->background == FRAME_BACKGROUND_PIXEL (s->f)
&& s->face->foreground == FRAME_FOREGROUND_PIXEL (s->f)
&& !s->cmp)
s->gc = s->f->output_data.mac->cursor_gc;
else
{
/* Cursor on non-default face: must merge. */
XGCValues xgcv;
unsigned long mask;
xgcv.background = s->f->output_data.mac->cursor_pixel;
xgcv.foreground = s->face->background;
/* If the glyph would be invisible, try a different foreground. */
if (xgcv.foreground == xgcv.background)
xgcv.foreground = s->face->foreground;
if (xgcv.foreground == xgcv.background)
xgcv.foreground = s->f->output_data.mac->cursor_foreground_pixel;
if (xgcv.foreground == xgcv.background)
xgcv.foreground = s->face->foreground;
/* Make sure the cursor is distinct from text in this face. */
if (xgcv.background == s->face->background
&& xgcv.foreground == s->face->foreground)
{
xgcv.background = s->face->foreground;
xgcv.foreground = s->face->background;
}
IF_DEBUG (x_check_font (s->f, s->font));
xgcv.font = s->font;
mask = GCForeground | GCBackground | GCFont;
if (FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc)
XChangeGC (s->display, FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc,
mask, &xgcv);
else
FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc
= XCreateGC (s->display, s->window, mask, &xgcv);
s->gc = FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc;
}
}
/* Set up S->gc of glyph string S for drawing text in mouse face. */
static void
x_set_mouse_face_gc (s)
struct glyph_string *s;
{
int face_id;
struct face *face;
/* What face has to be used last for the mouse face? */
face_id = FRAME_X_DISPLAY_INFO (s->f)->mouse_face_face_id;
face = FACE_FROM_ID (s->f, face_id);
if (face == NULL)
face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
if (s->first_glyph->type == CHAR_GLYPH)
face_id = FACE_FOR_CHAR (s->f, face, s->first_glyph->u.ch, -1, Qnil);
else
face_id = FACE_FOR_CHAR (s->f, face, 0, -1, Qnil);
s->face = FACE_FROM_ID (s->f, face_id);
PREPARE_FACE_FOR_DISPLAY (s->f, s->face);
/* If font in this face is same as S->font, use it. */
if (s->font == s->face->font)
s->gc = s->face->gc;
else
{
/* Otherwise construct scratch_cursor_gc with values from FACE
but font FONT. */
XGCValues xgcv;
unsigned long mask;
xgcv.background = s->face->background;
xgcv.foreground = s->face->foreground;
IF_DEBUG (x_check_font (s->f, s->font));
xgcv.font = s->font;
mask = GCForeground | GCBackground | GCFont;
if (FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc)
XChangeGC (s->display, FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc,
mask, &xgcv);
else
FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc
= XCreateGC (s->display, s->window, mask, &xgcv);
s->gc = FRAME_MAC_DISPLAY_INFO (s->f)->scratch_cursor_gc;
}
xassert (s->gc != 0);
}
/* Set S->gc of glyph string S to a GC suitable for drawing a mode line.
Faces to use in the mode line have already been computed when the
matrix was built, so there isn't much to do, here. */
static INLINE void
x_set_mode_line_face_gc (s)
struct glyph_string *s;
{
s->gc = s->face->gc;
}
/* Set S->gc of glyph string S for drawing that glyph string. Set
S->stippled_p to a non-zero value if the face of S has a stipple
pattern. */
static INLINE void
x_set_glyph_string_gc (s)
struct glyph_string *s;
{
PREPARE_FACE_FOR_DISPLAY (s->f, s->face);
if (s->hl == DRAW_NORMAL_TEXT)
{
s->gc = s->face->gc;
s->stippled_p = s->face->stipple != 0;
}
else if (s->hl == DRAW_INVERSE_VIDEO)
{
x_set_mode_line_face_gc (s);
s->stippled_p = s->face->stipple != 0;
}
else if (s->hl == DRAW_CURSOR)
{
x_set_cursor_gc (s);
s->stippled_p = 0;
}
else if (s->hl == DRAW_MOUSE_FACE)
{
x_set_mouse_face_gc (s);
s->stippled_p = s->face->stipple != 0;
}
else if (s->hl == DRAW_IMAGE_RAISED
|| s->hl == DRAW_IMAGE_SUNKEN)
{
s->gc = s->face->gc;
s->stippled_p = s->face->stipple != 0;
}
else
{
s->gc = s->face->gc;
s->stippled_p = s->face->stipple != 0;
}
/* GC must have been set. */
xassert (s->gc != 0);
}
/* Set clipping for output of glyph string S. S may be part of a mode
line or menu if we don't have X toolkit support. */
static INLINE void
x_set_glyph_string_clipping (s)
struct glyph_string *s;
{
Rect rects[MAX_CLIP_RECTS];
int n;
n = get_glyph_string_clip_rects (s, rects, MAX_CLIP_RECTS);
mac_set_clip_rectangles (s->f, s->gc, rects, n);
}
/* RIF:
Compute left and right overhang of glyph string S. If S is a glyph
string for a composition, assume overhangs don't exist. */
static void
mac_compute_glyph_string_overhangs (s)
struct glyph_string *s;
{
if (!(s->cmp == NULL
&& s->first_glyph->type == CHAR_GLYPH))
return;
if (!s->two_byte_p
#if USE_ATSUI
|| s->font->mac_style
#endif
)
{
XCharStruct cs;
mac_text_extents_16 (s->font, s->char2b, s->nchars, &cs);
s->right_overhang = cs.rbearing > cs.width ? cs.rbearing - cs.width : 0;
s->left_overhang = cs.lbearing < 0 ? -cs.lbearing : 0;
}
else
{
Rect r;
MacFontStruct *font = s->font;
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (s->f);
#endif
SetPortWindowPort (FRAME_MAC_WINDOW (s->f));
TextFont (font->mac_fontnum);
TextSize (font->mac_fontsize);
TextFace (font->mac_fontface);
QDTextBounds (s->nchars * 2, (char *)s->char2b, &r);
s->right_overhang = r.right > s->width ? r.right - s->width : 0;
s->left_overhang = r.left < 0 ? -r.left : 0;
}
}
/* Fill rectangle X, Y, W, H with background color of glyph string S. */
static INLINE void
x_clear_glyph_string_rect (s, x, y, w, h)
struct glyph_string *s;
int x, y, w, h;
{
mac_erase_rectangle (s->f, s->gc, x, y, w, h);
}
/* Draw the background of glyph_string S. If S->background_filled_p
is non-zero don't draw it. FORCE_P non-zero means draw the
background even if it wouldn't be drawn normally. This is used
when a string preceding S draws into the background of S, or S
contains the first component of a composition. */
static void
x_draw_glyph_string_background (s, force_p)
struct glyph_string *s;
int force_p;
{
/* Nothing to do if background has already been drawn or if it
shouldn't be drawn in the first place. */
if (!s->background_filled_p)
{
int box_line_width = max (s->face->box_line_width, 0);
#if 0 /* MAC_TODO: stipple */
if (s->stippled_p)
{
/* Fill background with a stipple pattern. */
XSetFillStyle (s->display, s->gc, FillOpaqueStippled);
XFillRectangle (s->display, s->window, s->gc, s->x,
s->y + box_line_width,
s->background_width,
s->height - 2 * box_line_width);
XSetFillStyle (s->display, s->gc, FillSolid);
s->background_filled_p = 1;
}
else
#endif
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)
{
x_clear_glyph_string_rect (s, s->x, s->y + box_line_width,
s->background_width,
s->height - 2 * box_line_width);
s->background_filled_p = 1;
}
}
}
/* Draw the foreground of glyph string S. */
static void
x_draw_glyph_string_foreground (s)
struct glyph_string *s;
{
int i, x, bg_width;
/* If first glyph of S has a left box line, start drawing the text
of S to the right of that box line. */
if (s->face->box != FACE_NO_BOX
&& s->first_glyph->left_box_line_p)
x = s->x + eabs (s->face->box_line_width);
else
x = s->x;
/* Draw characters of S as rectangles if S's font could not be
loaded. */
if (s->font_not_found_p)
{
for (i = 0; i < s->nchars; ++i)
{
struct glyph *g = s->first_glyph + i;
mac_draw_rectangle (s->f, s->gc, x, s->y,
g->pixel_width - 1, s->height - 1);
x += g->pixel_width;
}
}
else
{
char *char1b = (char *) s->char2b;
int boff = s->font_info->baseline_offset;
if (s->font_info->vertical_centering)
boff = VCENTER_BASELINE_OFFSET (s->font, s->f) - boff;
/* If we can use 8-bit functions, condense S->char2b. */
if (!s->two_byte_p
#if USE_ATSUI
&& GC_FONT (s->gc)->mac_style == NULL
#endif
)
for (i = 0; i < s->nchars; ++i)
char1b[i] = s->char2b[i].byte2;
/* Draw text with XDrawString if background has already been
filled. Otherwise, use XDrawImageString. (Note that
XDrawImageString is usually faster than XDrawString.) Always
use XDrawImageString when drawing the cursor so that there is
no chance that characters under a box cursor are invisible. */
if (s->for_overlaps
|| (s->background_filled_p && s->hl != DRAW_CURSOR))
bg_width = 0; /* Corresponds to XDrawString. */
else
bg_width = s->background_width; /* Corresponds to XDrawImageString. */
if (s->two_byte_p
#if USE_ATSUI
|| GC_FONT (s->gc)->mac_style
#endif
)
#if USE_CG_TEXT_DRAWING
if (!s->two_byte_p
&& mac_draw_image_string_cg (s->f, s->gc, x, s->ybase - boff,
s->char2b, s->nchars, bg_width,
s->face->overstrike))
;
else
#endif
mac_draw_image_string_16 (s->f, s->gc, x, s->ybase - boff,
s->char2b, s->nchars, bg_width,
s->face->overstrike);
else
mac_draw_image_string (s->f, s->gc, x, s->ybase - boff,
char1b, s->nchars, bg_width,
s->face->overstrike);
}
}
/* Draw the foreground of composite glyph string S. */
static void
x_draw_composite_glyph_string_foreground (s)
struct glyph_string *s;
{
int i, x;
/* If first glyph of S has a left box line, start drawing the text
of S to the right of that box line. */
if (s->face->box != FACE_NO_BOX
&& s->first_glyph->left_box_line_p)
x = s->x + eabs (s->face->box_line_width);
else
x = s->x;
/* S is a glyph string for a composition. S->gidx is the index of
the first character drawn for glyphs of this composition.
S->gidx == 0 means we are drawing the very first character of
this composition. */
/* Draw a rectangle for the composition if the font for the very
first character of the composition could not be loaded. */
if (s->font_not_found_p)
{
if (s->gidx == 0)
mac_draw_rectangle (s->f, s->gc, x, s->y,
s->width - 1, s->height - 1);
}
else
{
for (i = 0; i < s->nchars; i++, ++s->gidx)
if (mac_per_char_metric (GC_FONT (s->gc), s->char2b + i, 0) == NULL)
/* This is a nonexistent or zero-width glyph such as a
combining diacritic. Draw a rectangle. */
mac_draw_rectangle (s->f, s->gc,
x + s->cmp->offsets[s->gidx * 2], s->y,
FONT_WIDTH (GC_FONT (s->gc)) - 1, s->height - 1);
else
mac_draw_image_string_16 (s->f, s->gc,
x + s->cmp->offsets[s->gidx * 2],
s->ybase - s->cmp->offsets[s->gidx * 2 + 1],
s->char2b + i, 1, 0, s->face->overstrike);
}
}
#ifdef USE_X_TOOLKIT
static struct frame *x_frame_of_widget P_ ((Widget));
/* Return the frame on which widget WIDGET is used.. Abort if frame
cannot be determined. */
static struct frame *
x_frame_of_widget (widget)
Widget widget;
{
struct x_display_info *dpyinfo;
Lisp_Object tail;
struct frame *f;
dpyinfo = x_display_info_for_display (XtDisplay (widget));
/* Find the top-level shell of the widget. Note that this function
can be called when the widget is not yet realized, so XtWindow
(widget) == 0. That's the reason we can't simply use
x_any_window_to_frame. */
while (!XtIsTopLevelShell (widget))
widget = XtParent (widget);
/* Look for a frame with that top-level widget. Allocate the color
on that frame to get the right gamma correction value. */
for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
if (FRAMEP (XCAR (tail))
&& (f = XFRAME (XCAR (tail)),
(f->output_data.nothing != 1
&& FRAME_X_DISPLAY_INFO (f) == dpyinfo))
&& f->output_data.x->widget == widget)
return f;
abort ();
}
/* Allocate the color COLOR->pixel on the screen and display of
widget WIDGET in colormap CMAP. If an exact match cannot be
allocated, try the nearest color available. Value is non-zero
if successful. This is called from lwlib. */
int
x_alloc_nearest_color_for_widget (widget, cmap, color)
Widget widget;
Colormap cmap;
XColor *color;
{
struct frame *f = x_frame_of_widget (widget);
return x_alloc_nearest_color (f, cmap, color);
}
#endif /* USE_X_TOOLKIT */
#if 0 /* MAC_TODO */
/* Allocate the color COLOR->pixel on SCREEN of DISPLAY, colormap
CMAP. If an exact match can't be allocated, try the nearest color
available. Value is non-zero if successful. Set *COLOR to the
color allocated. */
int
x_alloc_nearest_color (f, cmap, color)
struct frame *f;
Colormap cmap;
XColor *color;
{
Display *display = FRAME_X_DISPLAY (f);
Screen *screen = FRAME_X_SCREEN (f);
int rc;
gamma_correct (f, color);
rc = XAllocColor (display, cmap, color);
if (rc == 0)
{
/* If we got to this point, the colormap is full, so we're going
to try to get the next closest color. The algorithm used is
a least-squares matching, which is what X uses for closest
color matching with StaticColor visuals. */
int nearest, i;
unsigned long nearest_delta = ~0;
int ncells = XDisplayCells (display, XScreenNumberOfScreen (screen));
XColor *cells = (XColor *) alloca (ncells * sizeof *cells);
for (i = 0; i < ncells; ++i)
cells[i].pixel = i;
XQueryColors (display, cmap, cells, ncells);
for (nearest = i = 0; i < ncells; ++i)
{
long dred = (color->red >> 8) - (cells[i].red >> 8);
long dgreen = (color->green >> 8) - (cells[i].green >> 8);
long dblue = (color->blue >> 8) - (cells[i].blue >> 8);
unsigned long delta = dred * dred + dgreen * dgreen + dblue * dblue;
if (delta < nearest_delta)
{
nearest = i;
nearest_delta = delta;
}
}
color->red = cells[nearest].red;
color->green = cells[nearest].green;
color->blue = cells[nearest].blue;
rc = XAllocColor (display, cmap, color);
}
#ifdef DEBUG_X_COLORS
if (rc)
register_color (color->pixel);
#endif /* DEBUG_X_COLORS */
return rc;
}
/* Allocate color PIXEL on frame F. PIXEL must already be allocated.
It's necessary to do this instead of just using PIXEL directly to
get color reference counts right. */
unsigned long
x_copy_color (f, pixel)
struct frame *f;
unsigned long pixel;
{
XColor color;
color.pixel = pixel;
BLOCK_INPUT;
XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &color);
XAllocColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &color);
UNBLOCK_INPUT;
#ifdef DEBUG_X_COLORS
register_color (pixel);
#endif
return color.pixel;
}
/* Allocate color PIXEL on display DPY. PIXEL must already be allocated.
It's necessary to do this instead of just using PIXEL directly to
get color reference counts right. */
unsigned long
x_copy_dpy_color (dpy, cmap, pixel)
Display *dpy;
Colormap cmap;
unsigned long pixel;
{
XColor color;
color.pixel = pixel;
BLOCK_INPUT;
XQueryColor (dpy, cmap, &color);
XAllocColor (dpy, cmap, &color);
UNBLOCK_INPUT;
#ifdef DEBUG_X_COLORS
register_color (pixel);
#endif
return color.pixel;
}
#endif /* MAC_TODO */
/* Brightness beyond which a color won't have its highlight brightness
boosted.
Nominally, highlight colors for `3d' faces are calculated by
brightening an object's color by a constant scale factor, but this
doesn't yield good results for dark colors, so for colors who's
brightness is less than this value (on a scale of 0-255) have to
use an additional additive factor.
The value here is set so that the default menu-bar/mode-line color
(grey75) will not have its highlights changed at all. */
#define HIGHLIGHT_COLOR_DARK_BOOST_LIMIT 187
/* Allocate a color which is lighter or darker than *COLOR by FACTOR
or DELTA. Try a color with RGB values multiplied by FACTOR first.
If this produces the same color as COLOR, try a color where all RGB
values have DELTA added. Return the allocated color in *COLOR.
DISPLAY is the X display, CMAP is the colormap to operate on.
Value is non-zero if successful. */
static int
mac_alloc_lighter_color (f, color, factor, delta)
struct frame *f;
unsigned long *color;
double factor;
int delta;
{
unsigned long new;
long bright;
/* On Mac, RGB values are 0-255, not 0-65535, so scale delta. */
delta /= 256;
/* Change RGB values by specified FACTOR. Avoid overflow! */
xassert (factor >= 0);
new = RGB_TO_ULONG (min (0xff, (int) (factor * RED_FROM_ULONG (*color))),
min (0xff, (int) (factor * GREEN_FROM_ULONG (*color))),
min (0xff, (int) (factor * BLUE_FROM_ULONG (*color))));
/* Calculate brightness of COLOR. */
bright = (2 * RED_FROM_ULONG (*color) + 3 * GREEN_FROM_ULONG (*color)
+ BLUE_FROM_ULONG (*color)) / 6;
/* We only boost colors that are darker than
HIGHLIGHT_COLOR_DARK_BOOST_LIMIT. */
if (bright < HIGHLIGHT_COLOR_DARK_BOOST_LIMIT)
/* Make an additive adjustment to NEW, because it's dark enough so
that scaling by FACTOR alone isn't enough. */
{
/* How far below the limit this color is (0 - 1, 1 being darker). */
double dimness = 1 - (double)bright / HIGHLIGHT_COLOR_DARK_BOOST_LIMIT;
/* The additive adjustment. */
int min_delta = delta * dimness * factor / 2;
if (factor < 1)
new = RGB_TO_ULONG (max (0, min (0xff, (int) (RED_FROM_ULONG (*color)) - min_delta)),
max (0, min (0xff, (int) (GREEN_FROM_ULONG (*color)) - min_delta)),
max (0, min (0xff, (int) (BLUE_FROM_ULONG (*color)) - min_delta)));
else
new = RGB_TO_ULONG (max (0, min (0xff, (int) (min_delta + RED_FROM_ULONG (*color)))),
max (0, min (0xff, (int) (min_delta + GREEN_FROM_ULONG (*color)))),
max (0, min (0xff, (int) (min_delta + BLUE_FROM_ULONG (*color)))));
}
if (new == *color)
new = RGB_TO_ULONG (max (0, min (0xff, (int) (delta + RED_FROM_ULONG (*color)))),
max (0, min (0xff, (int) (delta + GREEN_FROM_ULONG (*color)))),
max (0, min (0xff, (int) (delta + BLUE_FROM_ULONG (*color)))));
/* MAC_TODO: Map to palette and retry with delta if same? */
/* MAC_TODO: Free colors (if using palette)? */
if (new == *color)
return 0;
*color = new;
return 1;
}
/* Set up the foreground color for drawing relief lines of glyph
string S. RELIEF is a pointer to a struct relief containing the GC
with which lines will be drawn. Use a color that is FACTOR or
DELTA lighter or darker than the relief's background which is found
in S->f->output_data.x->relief_background. If such a color cannot
be allocated, use DEFAULT_PIXEL, instead. */
static void
x_setup_relief_color (f, relief, factor, delta, default_pixel)
struct frame *f;
struct relief *relief;
double factor;
int delta;
unsigned long default_pixel;
{
XGCValues xgcv;
struct mac_output *di = f->output_data.mac;
unsigned long mask = GCForeground;
unsigned long pixel;
unsigned long background = di->relief_background;
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
/* MAC_TODO: Free colors (if using palette)? */
/* Allocate new color. */
xgcv.foreground = default_pixel;
pixel = background;
if (dpyinfo->n_planes != 1
&& mac_alloc_lighter_color (f, &pixel, factor, delta))
{
relief->allocated_p = 1;
xgcv.foreground = relief->pixel = pixel;
}
if (relief->gc == 0)
{
#if 0 /* MAC_TODO: stipple */
xgcv.stipple = dpyinfo->gray;
mask |= GCStipple;
#endif
relief->gc = XCreateGC (NULL, FRAME_MAC_WINDOW (f), mask, &xgcv);
}
else
XChangeGC (NULL, relief->gc, mask, &xgcv);
}
/* Set up colors for the relief lines around glyph string S. */
static void
x_setup_relief_colors (s)
struct glyph_string *s;
{
struct mac_output *di = s->f->output_data.mac;
unsigned long color;
if (s->face->use_box_color_for_shadows_p)
color = s->face->box_color;
else if (s->first_glyph->type == IMAGE_GLYPH
&& s->img->pixmap
&& !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
color = IMAGE_BACKGROUND (s->img, s->f, 0);
else
{
XGCValues xgcv;
/* Get the background color of the face. */
XGetGCValues (s->display, s->gc, GCBackground, &xgcv);
color = xgcv.background;
}
if (di->white_relief.gc == 0
|| color != di->relief_background)
{
di->relief_background = color;
x_setup_relief_color (s->f, &di->white_relief, 1.2, 0x8000,
WHITE_PIX_DEFAULT (s->f));
x_setup_relief_color (s->f, &di->black_relief, 0.6, 0x4000,
BLACK_PIX_DEFAULT (s->f));
}
}
/* Draw a relief on frame F inside the rectangle given by LEFT_X,
TOP_Y, RIGHT_X, and BOTTOM_Y. WIDTH is the thickness of the relief
to draw, it must be >= 0. RAISED_P non-zero means draw a raised
relief. LEFT_P non-zero means draw a relief on the left side of
the rectangle. RIGHT_P non-zero means draw a relief on the right
side of the rectangle. CLIP_RECT is the clipping rectangle to use
when drawing. */
static void
x_draw_relief_rect (f, left_x, top_y, right_x, bottom_y, width,
raised_p, top_p, bot_p, left_p, right_p, clip_rect)
struct frame *f;
int left_x, top_y, right_x, bottom_y, width;
int top_p, bot_p, left_p, right_p, raised_p;
Rect *clip_rect;
{
Display *dpy = FRAME_MAC_DISPLAY (f);
int i;
GC gc;
if (raised_p)
gc = f->output_data.mac->white_relief.gc;
else
gc = f->output_data.mac->black_relief.gc;
mac_set_clip_rectangles (f, gc, clip_rect, 1);
/* Top. */
if (top_p)
for (i = 0; i < width; ++i)
mac_draw_line (f, gc,
left_x + i * left_p, top_y + i,
right_x + 1 - i * right_p, top_y + i);
/* Left. */
if (left_p)
for (i = 0; i < width; ++i)
mac_draw_line (f, gc,
left_x + i, top_y + i, left_x + i, bottom_y - i + 1);
mac_reset_clip_rectangles (f, gc);
if (raised_p)
gc = f->output_data.mac->black_relief.gc;
else
gc = f->output_data.mac->white_relief.gc;
mac_set_clip_rectangles (f, gc, clip_rect, 1);
/* Bottom. */
if (bot_p)
for (i = 0; i < width; ++i)
mac_draw_line (f, gc,
left_x + i * left_p, bottom_y - i,
right_x + 1 - i * right_p, bottom_y - i);
/* Right. */
if (right_p)
for (i = 0; i < width; ++i)
mac_draw_line (f, gc,
right_x - i, top_y + i + 1, right_x - i, bottom_y - i);
mac_reset_clip_rectangles (f, gc);
}
/* Draw a box on frame F inside the rectangle given by LEFT_X, TOP_Y,
RIGHT_X, and BOTTOM_Y. WIDTH is the thickness of the lines to
draw, it must be >= 0. LEFT_P non-zero means draw a line on the
left side of the rectangle. RIGHT_P non-zero means draw a line
on the right side of the rectangle. CLIP_RECT is the clipping
rectangle to use when drawing. */
static void
x_draw_box_rect (s, left_x, top_y, right_x, bottom_y, width,
left_p, right_p, clip_rect)
struct glyph_string *s;
int left_x, top_y, right_x, bottom_y, width, left_p, right_p;
Rect *clip_rect;
{
XGCValues xgcv;
XGetGCValues (s->display, s->gc, GCForeground, &xgcv);
XSetForeground (s->display, s->gc, s->face->box_color);
mac_set_clip_rectangles (s->f, s->gc, clip_rect, 1);
/* Top. */
mac_fill_rectangle (s->f, s->gc, left_x, top_y,
right_x - left_x + 1, width);
/* Left. */
if (left_p)
mac_fill_rectangle (s->f, s->gc, left_x, top_y,
width, bottom_y - top_y + 1);
/* Bottom. */
mac_fill_rectangle (s->f, s->gc, left_x, bottom_y - width + 1,
right_x - left_x + 1, width);
/* Right. */
if (right_p)
mac_fill_rectangle (s->f, s->gc, right_x - width + 1,
top_y, width, bottom_y - top_y + 1);
XSetForeground (s->display, s->gc, xgcv.foreground);
mac_reset_clip_rectangles (s->f, s->gc);
}
/* Draw a box around glyph string S. */
static void
x_draw_glyph_string_box (s)
struct glyph_string *s;
{
int width, left_x, right_x, top_y, bottom_y, last_x, raised_p;
int left_p, right_p;
struct glyph *last_glyph;
Rect clip_rect;
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));
/* The glyph that may have a right box line. */
last_glyph = (s->cmp || s->img
? s->first_glyph
: s->first_glyph + s->nchars - 1);
width = eabs (s->face->box_line_width);
raised_p = s->face->box == FACE_RAISED_BOX;
left_x = s->x;
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);
top_y = s->y;
bottom_y = top_y + s->height - 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)));
get_glyph_string_clip_rect (s, &clip_rect);
if (s->face->box == FACE_SIMPLE_BOX)
x_draw_box_rect (s, left_x, top_y, right_x, bottom_y, width,
left_p, right_p, &clip_rect);
else
{
x_setup_relief_colors (s);
x_draw_relief_rect (s->f, left_x, top_y, right_x, bottom_y,
width, raised_p, 1, 1, left_p, right_p, &clip_rect);
}
}
/* Draw foreground of image glyph string S. */
static void
x_draw_image_foreground (s)
struct glyph_string *s;
{
int x = s->x;
int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
/* If first glyph of S has a left box line, start drawing it to the
right of that line. */
if (s->face->box != FACE_NO_BOX
&& s->first_glyph->left_box_line_p
&& s->slice.x == 0)
x += eabs (s->face->box_line_width);
/* If there is a margin around the image, adjust x- and y-position
by that margin. */
if (s->slice.x == 0)
x += s->img->hmargin;
if (s->slice.y == 0)
y += s->img->vmargin;
if (s->img->pixmap)
{
x_set_glyph_string_clipping (s);
#if USE_CG_DRAWING
mac_draw_cg_image (s->img->data.ptr_val,
s->f, s->gc, s->slice.x, s->slice.y,
s->slice.width, s->slice.height, x, y, 1);
#endif
if (s->img->mask)
#if !USE_CG_DRAWING
mac_copy_area_with_mask (s->img->pixmap, s->img->mask,
s->f, s->gc, s->slice.x, s->slice.y,
s->slice.width, s->slice.height, x, y);
#else
;
#endif
else
{
#if !USE_CG_DRAWING
mac_copy_area (s->img->pixmap,
s->f, s->gc, s->slice.x, s->slice.y,
s->slice.width, s->slice.height, x, y);
#endif
/* When the image has a mask, we can expect that at
least part of a mouse highlight or a block cursor will
be visible. If the image doesn't have a mask, make
a block cursor visible by drawing a rectangle around
the image. I believe it's looking better if we do
nothing here for mouse-face. */
if (s->hl == DRAW_CURSOR)
{
int r = s->img->relief;
if (r < 0) r = -r;
mac_draw_rectangle (s->f, s->gc, x - r, y - r,
s->slice.width + r*2 - 1,
s->slice.height + r*2 - 1);
}
}
}
else
/* Draw a rectangle if image could not be loaded. */
mac_draw_rectangle (s->f, s->gc, x, y,
s->slice.width - 1, s->slice.height - 1);
}
/* Draw a relief around the image glyph string S. */
static void
x_draw_image_relief (s)
struct glyph_string *s;
{
int x0, y0, x1, y1, thick, raised_p;
Rect r;
int x = s->x;
int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
/* If first glyph of S has a left box line, start drawing it to the
right of that line. */
if (s->face->box != FACE_NO_BOX
&& s->first_glyph->left_box_line_p
&& s->slice.x == 0)
x += eabs (s->face->box_line_width);
/* If there is a margin around the image, adjust x- and y-position
by that margin. */
if (s->slice.x == 0)
x += s->img->hmargin;
if (s->slice.y == 0)
y += s->img->vmargin;
if (s->hl == DRAW_IMAGE_SUNKEN
|| s->hl == DRAW_IMAGE_RAISED)
{
thick = tool_bar_button_relief >= 0 ? tool_bar_button_relief : DEFAULT_TOOL_BAR_BUTTON_RELIEF;
raised_p = s->hl == DRAW_IMAGE_RAISED;
}
else
{
thick = eabs (s->img->relief);
raised_p = s->img->relief > 0;
}
x0 = x - thick;
y0 = y - thick;
x1 = x + s->slice.width + thick - 1;
y1 = y + s->slice.height + thick - 1;
x_setup_relief_colors (s);
get_glyph_string_clip_rect (s, &r);
x_draw_relief_rect (s->f, x0, y0, x1, y1, thick, 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,
&r);
}
/* Draw part of the background of glyph string S. X, Y, W, and H
give the rectangle to draw. */
static void
x_draw_glyph_string_bg_rect (s, x, y, w, h)
struct glyph_string *s;
int x, y, w, h;
{
#if 0 /* MAC_TODO: stipple */
if (s->stippled_p)
{
/* Fill background with a stipple pattern. */
XSetFillStyle (s->display, s->gc, FillOpaqueStippled);
XFillRectangle (s->display, s->window, s->gc, x, y, w, h);
XSetFillStyle (s->display, s->gc, FillSolid);
}
else
#endif /* MAC_TODO */
x_clear_glyph_string_rect (s, x, y, w, h);
}
/* Draw image glyph string S.
s->y
s->x +-------------------------
| s->face->box
|
| +-------------------------
| | s->img->margin
| |
| | +-------------------
| | | the image
*/
static void
x_draw_image_glyph_string (s)
struct glyph_string *s;
{
int x, y;
int box_line_hwidth = eabs (s->face->box_line_width);
int box_line_vwidth = max (s->face->box_line_width, 0);
int height;
height = s->height - 2 * box_line_vwidth;
/* Fill background with face under the image. Do it only if row is
taller than image or if image has a clip mask to reduce
flickering. */
s->stippled_p = s->face->stipple != 0;
if (height > s->slice.height
|| s->img->hmargin
|| s->img->vmargin
|| s->img->mask
|| s->img->pixmap == 0
|| s->width != s->background_width)
{
x = s->x;
if (s->first_glyph->left_box_line_p
&& s->slice.x == 0)
x += box_line_hwidth;
y = s->y;
if (s->slice.y == 0)
y += box_line_vwidth;
x_draw_glyph_string_bg_rect (s, x, y, s->background_width, height);
s->background_filled_p = 1;
}
/* Draw the foreground. */
x_draw_image_foreground (s);
/* If we must draw a relief around the image, do it. */
if (s->img->relief
|| s->hl == DRAW_IMAGE_RAISED
|| s->hl == DRAW_IMAGE_SUNKEN)
x_draw_image_relief (s);
}
/* Draw stretch glyph string S. */
static void
x_draw_stretch_glyph_string (s)
struct glyph_string *s;
{
xassert (s->first_glyph->type == STRETCH_GLYPH);
if (s->hl == DRAW_CURSOR
&& !x_stretch_cursor_p)
{
/* If `x-stretch-block-cursor' is nil, don't draw a block cursor
as wide as the stretch glyph. */
int width, background_width = s->background_width;
int x = s->x, left_x = window_box_left_offset (s->w, TEXT_AREA);
if (x < left_x)
{
background_width -= left_x - x;
x = left_x;
}
width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
/* Draw cursor. */
x_draw_glyph_string_bg_rect (s, x, s->y, width, s->height);
/* Clear rest using the GC of the original non-cursor face. */
if (width < background_width)
{
int y = s->y;
int w = background_width - width, h = s->height;
Rect r;
GC gc;
x += width;
if (s->row->mouse_face_p
&& cursor_in_mouse_face_p (s->w))
{
x_set_mouse_face_gc (s);
gc = s->gc;
}
else
gc = s->face->gc;
get_glyph_string_clip_rect (s, &r);
mac_set_clip_rectangles (s->f, gc, &r, 1);
#if 0 /* MAC_TODO: stipple */
if (s->face->stipple)
{
/* Fill background with a stipple pattern. */
XSetFillStyle (s->display, gc, FillOpaqueStippled);
XFillRectangle (s->display, s->window, gc, x, y, w, h);
XSetFillStyle (s->display, gc, FillSolid);
}
else
#endif /* MAC_TODO */
mac_erase_rectangle (s->f, gc, x, y, w, h);
}
}
else if (!s->background_filled_p)
{
int background_width = s->background_width;
int x = s->x, left_x = window_box_left_offset (s->w, TEXT_AREA);
/* Don't draw into left margin, fringe or scrollbar area
except for header line and mode line. */
if (x < left_x && !s->row->mode_line_p)
{
background_width -= left_x - x;
x = left_x;
}
if (background_width > 0)
x_draw_glyph_string_bg_rect (s, x, s->y, background_width, s->height);
}
s->background_filled_p = 1;
}
/* Draw glyph string S. */
static void
x_draw_glyph_string (s)
struct glyph_string *s;
{
int relief_drawn_p = 0;
/* If S draws into the background of its successor that does not
draw a cursor, draw the background of the successor first so that
S can draw into it. This makes S->next use XDrawString instead
of XDrawImageString. */
if (s->next && s->right_overhang && !s->for_overlaps
&& s->next->hl != DRAW_CURSOR)
{
xassert (s->next->img == NULL);
x_set_glyph_string_gc (s->next);
x_set_glyph_string_clipping (s->next);
x_draw_glyph_string_background (s->next, 1);
}
/* Set up S->gc, set clipping and draw S. */
x_set_glyph_string_gc (s);
/* Draw relief (if any) in advance for char/composition so that the
glyph string can be drawn over it. */
if (!s->for_overlaps
&& s->face->box != FACE_NO_BOX
&& (s->first_glyph->type == CHAR_GLYPH
|| s->first_glyph->type == COMPOSITE_GLYPH))
{
x_set_glyph_string_clipping (s);
x_draw_glyph_string_background (s, 1);
x_draw_glyph_string_box (s);
x_set_glyph_string_clipping (s);
relief_drawn_p = 1;
}
else
x_set_glyph_string_clipping (s);
switch (s->first_glyph->type)
{
case IMAGE_GLYPH:
x_draw_image_glyph_string (s);
break;
case STRETCH_GLYPH:
x_draw_stretch_glyph_string (s);
break;
case CHAR_GLYPH:
if (s->for_overlaps)
s->background_filled_p = 1;
else
x_draw_glyph_string_background (s, 0);
x_draw_glyph_string_foreground (s);
break;
case COMPOSITE_GLYPH:
if (s->for_overlaps || s->gidx > 0)
s->background_filled_p = 1;
else
x_draw_glyph_string_background (s, 1);
x_draw_composite_glyph_string_foreground (s);
break;
default:
abort ();
}
if (!s->for_overlaps)
{
/* Draw underline. */
if (s->face->underline_p)
{
unsigned long tem, h;
int y;
#if 0
/* Get the underline thickness. Default is 1 pixel. */
if (!XGetFontProperty (s->font, XA_UNDERLINE_THICKNESS, &h))
#endif
h = 1;
y = s->y + s->height - h;
if (!x_underline_at_descent_line)
{
/* Get the underline position. This is the recommended
vertical offset in pixels from the baseline to the top of
the underline. This is a signed value according to the
specs, and its default is
ROUND ((maximum descent) / 2), with
ROUND(x) = floor (x + 0.5) */
#if 0
if (x_use_underline_position_properties
&& XGetFontProperty (s->font, XA_UNDERLINE_POSITION, &tem))
y = s->ybase + (long) tem;
else
#endif
if (s->face->font)
y = s->ybase + (s->face->font->max_bounds.descent + 1) / 2;
}
if (s->face->underline_defaulted_p)
mac_fill_rectangle (s->f, s->gc, s->x, y,
s->background_width, h);
else
{
XGCValues xgcv;
XGetGCValues (s->display, s->gc, GCForeground, &xgcv);
XSetForeground (s->display, s->gc, s->face->underline_color);
mac_fill_rectangle (s->f, s->gc, s->x, y,
s->background_width, h);
XSetForeground (s->display, s->gc, xgcv.foreground);
}
}
/* Draw overline. */
if (s->face->overline_p)
{
unsigned long dy = 0, h = 1;
if (s->face->overline_color_defaulted_p)
mac_fill_rectangle (s->f, s->gc, s->x, s->y + dy,
s->background_width, h);
else
{
XGCValues xgcv;
XGetGCValues (s->display, s->gc, GCForeground, &xgcv);
XSetForeground (s->display, s->gc, s->face->overline_color);
mac_fill_rectangle (s->f, s->gc, s->x, s->y + dy,
s->background_width, h);
XSetForeground (s->display, s->gc, xgcv.foreground);
}
}
/* Draw strike-through. */
if (s->face->strike_through_p)
{
unsigned long h = 1;
unsigned long dy = (s->height - h) / 2;
if (s->face->strike_through_color_defaulted_p)
mac_fill_rectangle (s->f, s->gc, s->x, s->y + dy,
s->width, h);
else
{
XGCValues xgcv;
XGetGCValues (s->display, s->gc, GCForeground, &xgcv);
XSetForeground (s->display, s->gc, s->face->strike_through_color);
mac_fill_rectangle (s->f, s->gc, s->x, s->y + dy,
s->width, h);
XSetForeground (s->display, s->gc, xgcv.foreground);
}
}
/* Draw relief if not yet drawn. */
if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
x_draw_glyph_string_box (s);
}
/* Reset clipping. */
mac_reset_clip_rectangles (s->f, s->gc);
}
/* Shift display to make room for inserted glyphs. */
void
mac_shift_glyphs_for_insert (f, x, y, width, height, shift_by)
struct frame *f;
int x, y, width, height, shift_by;
{
mac_scroll_area (f, f->output_data.mac->normal_gc,
x, y, width, height,
x + shift_by, y);
}
/* Delete N glyphs at the nominal cursor position. Not implemented
for X frames. */
static void
x_delete_glyphs (n)
register int n;
{
abort ();
}
/* Clear entire frame. If updating_frame is non-null, clear that
frame. Otherwise clear the selected frame. */
static void
x_clear_frame (struct frame *f)
{
/* Clearing the frame will erase any cursor, so mark them all as no
longer visible. */
mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
output_cursor.hpos = output_cursor.vpos = 0;
output_cursor.x = -1;
/* We don't set the output cursor here because there will always
follow an explicit cursor_to. */
BLOCK_INPUT;
mac_clear_window (f);
/* We have to clear the scroll bars, too. If we have changed
colors or something like that, then they should be notified. */
x_scroll_bar_clear (f);
XFlush (FRAME_MAC_DISPLAY (f));
UNBLOCK_INPUT;
}
/* Invert the middle quarter of the frame for .15 sec. */
/* We use the select system call to do the waiting, so we have to make
sure it's available. If it isn't, we just won't do visual bells. */
#if defined (HAVE_TIMEVAL) && defined (HAVE_SELECT)
/* Subtract the `struct timeval' values X and Y, storing the result in
*RESULT. Return 1 if the difference is negative, otherwise 0. */
static int
timeval_subtract (result, x, y)
struct timeval *result, x, y;
{
/* 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;
}
void
XTflash (f)
struct frame *f;
{
/* Get the height not including a menu bar widget. */
int height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, FRAME_LINES (f));
/* Height of each line to flash. */
int flash_height = FRAME_LINE_HEIGHT (f);
/* These will be the left and right margins of the rectangles. */
int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f);
int width;
/* Don't flash the area between a scroll bar and the frame
edge it is next to. */
switch (FRAME_VERTICAL_SCROLL_BAR_TYPE (f))
{
case vertical_scroll_bar_left:
flash_left += VERTICAL_SCROLL_BAR_WIDTH_TRIM;
break;
case vertical_scroll_bar_right:
flash_right -= VERTICAL_SCROLL_BAR_WIDTH_TRIM;
break;
default:
break;
}
width = flash_right - flash_left;
BLOCK_INPUT;
/* If window is tall, flash top and bottom line. */
if (height > 3 * FRAME_LINE_HEIGHT (f))
{
mac_invert_rectangle (f, flash_left,
(FRAME_INTERNAL_BORDER_WIDTH (f)
+ FRAME_TOOL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f)),
width, flash_height);
mac_invert_rectangle (f, flash_left,
(height - flash_height
- FRAME_INTERNAL_BORDER_WIDTH (f)),
width, flash_height);
}
else
/* If it is short, flash it all. */
mac_invert_rectangle (f, flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
width, height - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
x_flush (f);
{
struct timeval wakeup;
EMACS_GET_TIME (wakeup);
/* Compute time to wait until, propagating carry from usecs. */
wakeup.tv_usec += 150000;
wakeup.tv_sec += (wakeup.tv_usec / 1000000);
wakeup.tv_usec %= 1000000;
/* Keep waiting until past the time wakeup or any input gets
available. */
while (! detect_input_pending ())
{
struct timeval current;
struct timeval timeout;
EMACS_GET_TIME (current);
/* Break if result would be negative. */
if (timeval_subtract (¤t, wakeup, current))
break;
/* How long `select' should wait. */
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
/* Try to wait that long--but we might wake up sooner. */
select (0, NULL, NULL, NULL, &timeout);
}
}
/* If window is tall, flash top and bottom line. */
if (height > 3 * FRAME_LINE_HEIGHT (f))
{
mac_invert_rectangle (f, flash_left,
(FRAME_INTERNAL_BORDER_WIDTH (f)
+ FRAME_TOOL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f)),
width, flash_height);
mac_invert_rectangle (f, flash_left,
(height - flash_height
- FRAME_INTERNAL_BORDER_WIDTH (f)),
width, flash_height);
}
else
/* If it is short, flash it all. */
mac_invert_rectangle (f, flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
width, height - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
x_flush (f);
UNBLOCK_INPUT;
}
#endif /* defined (HAVE_TIMEVAL) && defined (HAVE_SELECT) */
/* Make audible bell. */
void
XTring_bell ()
{
struct frame *f = SELECTED_FRAME ();
#if defined (HAVE_TIMEVAL) && defined (HAVE_SELECT)
if (visible_bell)
XTflash (f);
else
#endif
{
BLOCK_INPUT;
SysBeep (1);
XFlush (FRAME_MAC_DISPLAY (f));
UNBLOCK_INPUT;
}
}
/* Specify how many text lines, from the top of the window,
should be affected by insert-lines and delete-lines operations.
This, and those operations, are used only within an update
that is bounded by calls to x_update_begin and x_update_end. */
static void
XTset_terminal_window (n)
register int n;
{
/* This function intentionally left blank. */
}
/***********************************************************************
Line Dance
***********************************************************************/
/* Perform an insert-lines or delete-lines operation, inserting N
lines or deleting -N lines at vertical position VPOS. */
static void
x_ins_del_lines (vpos, n)
int vpos, n;
{
abort ();
}
/* Scroll part of the display as described by RUN. */
static void
x_scroll_run (w, run)
struct window *w;
struct run *run;
{
struct frame *f = XFRAME (w->frame);
int x, y, width, height, from_y, to_y, bottom_y;
/* 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;
}
BLOCK_INPUT;
/* Cursor off. Will be switched on again in x_update_window_end. */
updated_window = w;
x_clear_cursor (w);
mac_scroll_area (f, f->output_data.mac->normal_gc,
x, from_y,
width, height,
x, to_y);
UNBLOCK_INPUT;
}
/***********************************************************************
Exposure Events
***********************************************************************/
static void
frame_highlight (f)
struct frame *f;
{
x_update_cursor (f, 1);
}
static void
frame_unhighlight (f)
struct frame *f;
{
x_update_cursor (f, 1);
}
/* The focus has changed. Update the frames as necessary to reflect
the new situation. Note that we can't change the selected frame
here, because the Lisp code we are interrupting might become confused.
Each event gets marked with the frame in which it occurred, so the
Lisp code can tell when the switch took place by examining the events. */
static void
x_new_focus_frame (dpyinfo, frame)
struct x_display_info *dpyinfo;
struct frame *frame;
{
struct frame *old_focus = dpyinfo->x_focus_frame;
if (frame != dpyinfo->x_focus_frame)
{
/* Set this before calling other routines, so that they see
the correct value of x_focus_frame. */
dpyinfo->x_focus_frame = frame;
if (old_focus && old_focus->auto_lower)
x_lower_frame (old_focus);
#if 0
selected_frame = frame;
XSETFRAME (XWINDOW (selected_frame->selected_window)->frame,
selected_frame);
Fselect_window (selected_frame->selected_window, Qnil);
choose_minibuf_frame ();
#endif /* ! 0 */
if (dpyinfo->x_focus_frame && dpyinfo->x_focus_frame->auto_raise)
pending_autoraise_frame = dpyinfo->x_focus_frame;
else
pending_autoraise_frame = 0;
#if USE_MAC_FONT_PANEL
if (frame)
mac_set_font_info_for_selection (frame, DEFAULT_FACE_ID, 0);
#endif
}
x_frame_rehighlight (dpyinfo);
}
/* Handle FocusIn and FocusOut state changes for FRAME.
If FRAME has focus and there exists more than one frame, puts
a FOCUS_IN_EVENT into *BUFP. */
static void
mac_focus_changed (type, dpyinfo, frame, bufp)
int type;
struct mac_display_info *dpyinfo;
struct frame *frame;
struct input_event *bufp;
{
if (type == activeFlag)
{
if (dpyinfo->x_focus_event_frame != frame)
{
x_new_focus_frame (dpyinfo, frame);
dpyinfo->x_focus_event_frame = frame;
/* Don't stop displaying the initial startup message
for a switch-frame event we don't need. */
if (NILP (Vterminal_frame)
&& CONSP (Vframe_list)
&& !NILP (XCDR (Vframe_list)))
{
bufp->kind = FOCUS_IN_EVENT;
XSETFRAME (bufp->frame_or_window, frame);
}
}
}
else
{
if (dpyinfo->x_focus_event_frame == frame)
{
dpyinfo->x_focus_event_frame = 0;
x_new_focus_frame (dpyinfo, 0);
}
}
}
/* The focus may have changed. Figure out if it is a real focus change,
by checking both FocusIn/Out and Enter/LeaveNotify events.
Returns FOCUS_IN_EVENT event in *BUFP. */
static void
x_detect_focus_change (dpyinfo, event, bufp)
struct mac_display_info *dpyinfo;
const EventRecord *event;
struct input_event *bufp;
{
struct frame *frame;
frame = mac_window_to_frame ((WindowRef) event->message);
if (! frame)
return;
/* On Mac, this is only called from focus events, so no switch needed. */
mac_focus_changed ((event->modifiers & activeFlag),
dpyinfo, frame, bufp);
}
/* Handle an event saying the mouse has moved out of an Emacs frame. */
void
x_mouse_leave (dpyinfo)
struct x_display_info *dpyinfo;
{
x_new_focus_frame (dpyinfo, dpyinfo->x_focus_event_frame);
}
/* The focus has changed, or we have redirected a frame's focus to
another frame (this happens when a frame uses a surrogate
mini-buffer frame). Shift the highlight as appropriate.
The FRAME argument doesn't necessarily have anything to do with which
frame is being highlighted or un-highlighted; we only use it to find
the appropriate X display info. */
static void
XTframe_rehighlight (frame)
struct frame *frame;
{
x_frame_rehighlight (FRAME_X_DISPLAY_INFO (frame));
}
static void
x_frame_rehighlight (dpyinfo)
struct x_display_info *dpyinfo;
{
struct frame *old_highlight = dpyinfo->x_highlight_frame;
if (dpyinfo->x_focus_frame)
{
dpyinfo->x_highlight_frame
= ((FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame)))
? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
: dpyinfo->x_focus_frame);
if (! FRAME_LIVE_P (dpyinfo->x_highlight_frame))
{
FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame) = Qnil;
dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
}
}
else
dpyinfo->x_highlight_frame = 0;
if (dpyinfo->x_highlight_frame != old_highlight)
{
if (old_highlight)
frame_unhighlight (old_highlight);
if (dpyinfo->x_highlight_frame)
frame_highlight (dpyinfo->x_highlight_frame);
}
}
/* Convert a keysym to its name. */
char *
x_get_keysym_name (keysym)
int keysym;
{
char *value;
BLOCK_INPUT;
#if 0
value = XKeysymToString (keysym);
#else
value = 0;
#endif
UNBLOCK_INPUT;
return value;
}
/* Function to report a mouse movement to the mainstream Emacs code.
The input handler calls this.
We have received a mouse movement event, which is given in *event.
If the mouse is over a different glyph than it was last time, tell
the mainstream emacs code by setting mouse_moved. If not, ask for
another motion event, so we can check again the next time it moves. */
static Point last_mouse_motion_position;
static Lisp_Object last_mouse_motion_frame;
static int
note_mouse_movement (frame, pos)
FRAME_PTR frame;
Point *pos;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (frame);
#if TARGET_API_MAC_CARBON
Rect r;
#endif
last_mouse_movement_time = TickCount () * (1000 / 60); /* to milliseconds */
last_mouse_motion_position = *pos;
XSETFRAME (last_mouse_motion_frame, frame);
if (frame == dpyinfo->mouse_face_mouse_frame
#if TARGET_API_MAC_CARBON
&& !PtInRect (*pos, GetWindowPortBounds (FRAME_MAC_WINDOW (frame), &r))
#else
&& !PtInRect (*pos, &FRAME_MAC_WINDOW (frame)->portRect)
#endif
)
{
/* This case corresponds to LeaveNotify in X11. If we move
outside the frame, then we're certainly no longer on any text
in the frame. */
clear_mouse_face (dpyinfo);
dpyinfo->mouse_face_mouse_frame = 0;
if (!dpyinfo->grabbed)
FRAME_RIF (frame)->define_frame_cursor (frame,
frame->output_data.mac->nontext_cursor);
}
/* Has the mouse moved off the glyph it was on at the last sighting? */
if (frame != last_mouse_glyph_frame
|| !PtInRect (*pos, &last_mouse_glyph))
{
frame->mouse_moved = 1;
last_mouse_scroll_bar = Qnil;
note_mouse_highlight (frame, pos->h, pos->v);
/* Remember which glyph we're now on. */
remember_mouse_glyph (frame, pos->h, pos->v, &last_mouse_glyph);
last_mouse_glyph_frame = frame;
return 1;
}
return 0;
}
/************************************************************************
Mouse Face
************************************************************************/
/* MAC TODO: This should be called from somewhere (or removed) ++KFS */
static void
redo_mouse_highlight ()
{
if (!NILP (last_mouse_motion_frame)
&& FRAME_LIVE_P (XFRAME (last_mouse_motion_frame)))
note_mouse_highlight (XFRAME (last_mouse_motion_frame),
last_mouse_motion_position.h,
last_mouse_motion_position.v);
}
static struct frame *
mac_focus_frame (dpyinfo)
struct mac_display_info *dpyinfo;
{
if (dpyinfo->x_focus_frame)
return dpyinfo->x_focus_frame;
else
/* Mac version may get events, such as a menu bar click, even when
all the frames are invisible. In this case, we regard the
event came to the selected frame. */
return SELECTED_FRAME ();
}
/* Return the current position of the mouse.
*FP should be a frame which indicates which display to ask about.
If the mouse movement started in a scroll bar, set *FP, *BAR_WINDOW,
and *PART to the frame, window, and scroll bar part that the mouse
is over. Set *X and *Y to the portion and whole of the mouse's
position on the scroll bar.
If the mouse movement started elsewhere, set *FP to the frame the
mouse is on, *BAR_WINDOW to nil, and *X and *Y to the character cell
the mouse is over.
Set *TIME to the server time-stamp for the time at which the mouse
was at this position.
Don't store anything if we don't have a valid set of values to report.
This clears the mouse_moved flag, so we can wait for the next mouse
movement. */
static void
XTmouse_position (fp, insist, bar_window, part, x, y, time)
FRAME_PTR *fp;
int insist;
Lisp_Object *bar_window;
enum scroll_bar_part *part;
Lisp_Object *x, *y;
unsigned long *time;
{
FRAME_PTR f1;
BLOCK_INPUT;
if (! NILP (last_mouse_scroll_bar) && insist == 0)
x_scroll_bar_report_motion (fp, bar_window, part, x, y, time);
else
{
Lisp_Object frame, tail;
/* Clear the mouse-moved flag for every frame on this display. */
FOR_EACH_FRAME (tail, frame)
XFRAME (frame)->mouse_moved = 0;
last_mouse_scroll_bar = Qnil;
if (FRAME_MAC_DISPLAY_INFO (*fp)->grabbed && last_mouse_frame
&& FRAME_LIVE_P (last_mouse_frame))
f1 = last_mouse_frame;
else
f1 = mac_focus_frame (FRAME_MAC_DISPLAY_INFO (*fp));
if (f1)
{
/* Ok, we found a frame. Store all the values.
last_mouse_glyph is a rectangle used to reduce the
generation of mouse events. To not miss any motion
events, we must divide the frame into rectangles of the
size of the smallest character that could be displayed
on it, i.e. into the same rectangles that matrices on
the frame are divided into. */
Point mouse_pos;
#if TARGET_API_MAC_CARBON
GetGlobalMouse (&mouse_pos);
mouse_pos.h -= f1->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f1);
mouse_pos.v -= f1->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f1);
#else
SetPortWindowPort (FRAME_MAC_WINDOW (f1));
GetMouse (&mouse_pos);
#endif
remember_mouse_glyph (f1, mouse_pos.h, mouse_pos.v,
&last_mouse_glyph);
last_mouse_glyph_frame = f1;
*bar_window = Qnil;
*part = 0;
*fp = f1;
XSETINT (*x, mouse_pos.h);
XSETINT (*y, mouse_pos.v);
*time = last_mouse_movement_time;
}
}
UNBLOCK_INPUT;
}
/************************************************************************
Toolkit scroll bars
************************************************************************/
#ifdef USE_TOOLKIT_SCROLL_BARS
static pascal void scroll_bar_timer_callback P_ ((EventLoopTimerRef, void *));
static OSStatus install_scroll_bar_timer P_ ((void));
static OSStatus set_scroll_bar_timer P_ ((EventTimerInterval));
static int control_part_code_to_scroll_bar_part P_ ((ControlPartCode));
static void construct_scroll_bar_click P_ ((struct scroll_bar *, int,
struct input_event *));
static OSStatus get_control_part_bounds P_ ((ControlRef, ControlPartCode,
Rect *));
static void x_scroll_bar_handle_press P_ ((struct scroll_bar *,
ControlPartCode, Point,
struct input_event *));
static void x_scroll_bar_handle_release P_ ((struct scroll_bar *,
struct input_event *));
static void x_scroll_bar_handle_drag P_ ((WindowRef, struct scroll_bar *,
Point, struct input_event *));
static void x_set_toolkit_scroll_bar_thumb P_ ((struct scroll_bar *,
int, int, int));
/* Last scroll bar part sent in x_scroll_bar_handle_*. */
static int last_scroll_bar_part;
static EventLoopTimerRef scroll_bar_timer;
static int scroll_bar_timer_event_posted_p;
#define SCROLL_BAR_FIRST_DELAY 0.5
#define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
static pascal void
scroll_bar_timer_callback (timer, data)
EventLoopTimerRef timer;
void *data;
{
OSStatus err;
err = mac_post_mouse_moved_event ();
if (err == noErr)
scroll_bar_timer_event_posted_p = 1;
}
static OSStatus
install_scroll_bar_timer ()
{
static EventLoopTimerUPP scroll_bar_timer_callbackUPP = NULL;
if (scroll_bar_timer_callbackUPP == NULL)
scroll_bar_timer_callbackUPP =
NewEventLoopTimerUPP (scroll_bar_timer_callback);
if (scroll_bar_timer == NULL)
/* Mac OS X and CarbonLib 1.5 and later allow us to specify
kEventDurationForever as delays. */
return
InstallEventLoopTimer (GetCurrentEventLoop (),
kEventDurationForever, kEventDurationForever,
scroll_bar_timer_callbackUPP, NULL,
&scroll_bar_timer);
}
static OSStatus
set_scroll_bar_timer (delay)
EventTimerInterval delay;
{
if (scroll_bar_timer == NULL)
install_scroll_bar_timer ();
scroll_bar_timer_event_posted_p = 0;
return SetEventLoopTimerNextFireTime (scroll_bar_timer, delay);
}
static int
control_part_code_to_scroll_bar_part (part_code)
ControlPartCode part_code;
{
switch (part_code)
{
case kControlUpButtonPart: return scroll_bar_up_arrow;
case kControlDownButtonPart: return scroll_bar_down_arrow;
case kControlPageUpPart: return scroll_bar_above_handle;
case kControlPageDownPart: return scroll_bar_below_handle;
case kControlIndicatorPart: return scroll_bar_handle;
}
return -1;
}
static void
construct_scroll_bar_click (bar, part, bufp)
struct scroll_bar *bar;
int part;
struct input_event *bufp;
{
bufp->kind = SCROLL_BAR_CLICK_EVENT;
bufp->frame_or_window = bar->window;
bufp->arg = Qnil;
bufp->part = part;
bufp->code = 0;
XSETINT (bufp->x, 0);
XSETINT (bufp->y, 0);
bufp->modifiers = 0;
}
static OSStatus
get_control_part_bounds (ch, part_code, rect)
ControlRef ch;
ControlPartCode part_code;
Rect *rect;
{
RgnHandle region = NewRgn ();
OSStatus err;
err = GetControlRegion (ch, part_code, region);
if (err == noErr)
GetRegionBounds (region, rect);
DisposeRgn (region);
return err;
}
static void
x_scroll_bar_handle_press (bar, part_code, mouse_pos, bufp)
struct scroll_bar *bar;
ControlPartCode part_code;
Point mouse_pos;
struct input_event *bufp;
{
int part = control_part_code_to_scroll_bar_part (part_code);
if (part < 0)
return;
if (part != scroll_bar_handle)
{
construct_scroll_bar_click (bar, part, bufp);
HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code);
set_scroll_bar_timer (SCROLL_BAR_FIRST_DELAY);
bar->dragging = Qnil;
}
else
{
Rect r;
get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar),
kControlIndicatorPart, &r);
XSETINT (bar->dragging, - (mouse_pos.v - r.top) - 1);
}
last_scroll_bar_part = part;
tracked_scroll_bar = bar;
}
static void
x_scroll_bar_handle_release (bar, bufp)
struct scroll_bar *bar;
struct input_event *bufp;
{
if (last_scroll_bar_part != scroll_bar_handle
|| (INTEGERP (bar->dragging) && XINT (bar->dragging) >= 0))
construct_scroll_bar_click (bar, scroll_bar_end_scroll, bufp);
HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0);
set_scroll_bar_timer (kEventDurationForever);
last_scroll_bar_part = -1;
bar->dragging = Qnil;
tracked_scroll_bar = NULL;
}
static void
x_scroll_bar_handle_drag (win, bar, mouse_pos, bufp)
WindowRef win;
struct scroll_bar *bar;
Point mouse_pos;
struct input_event *bufp;
{
ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
if (last_scroll_bar_part == scroll_bar_handle)
{
int top, top_range;
Rect r;
get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar),
kControlIndicatorPart, &r);
if (INTEGERP (bar->dragging) && XINT (bar->dragging) < 0)
XSETINT (bar->dragging, - (XINT (bar->dragging) + 1));
top = mouse_pos.v - XINT (bar->dragging) - XINT (bar->track_top);
top_range = XINT (bar->track_height) - XINT (bar->min_handle);
if (top < 0)
top = 0;
if (top > top_range)
top = top_range;
construct_scroll_bar_click (bar, scroll_bar_handle, bufp);
XSETINT (bufp->x, top);
XSETINT (bufp->y, top_range);
}
else
{
ControlPartCode part_code;
int unhilite_p = 0, part;
if (ch != FindControlUnderMouse (mouse_pos, win, &part_code))
unhilite_p = 1;
else
{
part = control_part_code_to_scroll_bar_part (part_code);
switch (last_scroll_bar_part)
{
case scroll_bar_above_handle:
case scroll_bar_below_handle:
if (part != scroll_bar_above_handle
&& part != scroll_bar_below_handle)
unhilite_p = 1;
break;
case scroll_bar_up_arrow:
case scroll_bar_down_arrow:
if (part != scroll_bar_up_arrow
&& part != scroll_bar_down_arrow)
unhilite_p = 1;
break;
}
}
if (unhilite_p)
HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0);
else if (part != last_scroll_bar_part
|| scroll_bar_timer_event_posted_p)
{
construct_scroll_bar_click (bar, part, bufp);
last_scroll_bar_part = part;
HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code);
set_scroll_bar_timer (SCROLL_BAR_CONTINUOUS_DELAY);
}
}
}
/* Set the thumb size and position of scroll bar BAR. We are currently
displaying PORTION out of a whole WHOLE, and our position POSITION. */
static void
x_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
struct scroll_bar *bar;
int portion, position, whole;
{
ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
int value, viewsize, maximum;
if (XINT (bar->track_height) == 0)
return;
if (whole <= portion)
value = 0, viewsize = 1, maximum = 0;
else
{
float scale;
maximum = XINT (bar->track_height) - XINT (bar->min_handle);
scale = (float) maximum / (whole - portion);
value = position * scale + 0.5f;
viewsize = (int) (portion * scale + 0.5f) + XINT (bar->min_handle);
}
BLOCK_INPUT;
if (GetControlViewSize (ch) != viewsize
|| GetControl32BitValue (ch) != value
|| GetControl32BitMaximum (ch) != maximum)
{
/* Temporarily hide the scroll bar to avoid multiple redraws. */
SetControlVisibility (ch, false, false);
SetControl32BitMaximum (ch, maximum);
SetControl32BitValue (ch, value);
SetControlViewSize (ch, viewsize);
SetControlVisibility (ch, true, true);
}
UNBLOCK_INPUT;
}
#endif /* USE_TOOLKIT_SCROLL_BARS */
/************************************************************************
Scroll bars, general
************************************************************************/
/* Create a scroll bar and return the scroll bar vector for it. W is
the Emacs window on which to create the scroll bar. TOP, LEFT,
WIDTH and HEIGHT are the pixel coordinates and dimensions of the
scroll bar. */
static struct scroll_bar *
x_scroll_bar_create (w, top, left, width, height, disp_top, disp_height)
struct window *w;
int top, left, width, height, disp_top, disp_height;
{
struct frame *f = XFRAME (w->frame);
struct scroll_bar *bar
= XSCROLL_BAR (Fmake_vector (make_number (SCROLL_BAR_VEC_SIZE), Qnil));
Rect r;
ControlRef ch;
BLOCK_INPUT;
r.left = left;
r.top = disp_top;
r.right = left + width;
r.bottom = disp_top + disp_height;
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
#if TARGET_API_MAC_CARBON
ch = NewControl (FRAME_MAC_WINDOW (f), &r, "\p",
#ifdef USE_TOOLKIT_SCROLL_BARS
false,
#else
width < disp_height,
#endif
0, 0, 0, kControlScrollBarProc, (long) bar);
#else
ch = NewControl (FRAME_MAC_WINDOW (f), &r, "\p", width < disp_height,
0, 0, 0, scrollBarProc, (long) bar);
#endif
SET_SCROLL_BAR_CONTROL_REF (bar, ch);
XSETWINDOW (bar->window, w);
XSETINT (bar->top, top);
XSETINT (bar->left, left);
XSETINT (bar->width, width);
XSETINT (bar->height, height);
XSETINT (bar->start, 0);
XSETINT (bar->end, 0);
bar->dragging = Qnil;
#ifdef MAC_OSX
bar->fringe_extended_p = Qnil;
#endif
bar->redraw_needed_p = Qnil;
#ifdef USE_TOOLKIT_SCROLL_BARS
bar->track_top = Qnil;
bar->track_height = Qnil;
bar->min_handle = Qnil;
#endif
/* Add bar to its frame's list of scroll bars. */
bar->next = FRAME_SCROLL_BARS (f);
bar->prev = Qnil;
XSETVECTOR (FRAME_SCROLL_BARS (f), bar);
if (!NILP (bar->next))
XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);
UNBLOCK_INPUT;
return bar;
}
/* Draw BAR's handle in the proper position.
If the handle is already drawn from START to END, don't bother
redrawing it, unless REBUILD is non-zero; in that case, always
redraw it. (REBUILD is handy for drawing the handle after expose
events.)
Normally, we want to constrain the start and end of the handle to
fit inside its rectangle, but if the user is dragging the scroll
bar handle, we want to let them drag it down all the way, so that
the bar's top is as far down as it goes; otherwise, there's no way
to move to the very end of the buffer. */
#ifndef USE_TOOLKIT_SCROLL_BARS
static void
x_scroll_bar_set_handle (bar, start, end, rebuild)
struct scroll_bar *bar;
int start, end;
int rebuild;
{
int dragging = ! NILP (bar->dragging);
ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height));
int length = end - start;
/* If the display is already accurate, do nothing. */
if (! rebuild
&& start == XINT (bar->start)
&& end == XINT (bar->end))
return;
BLOCK_INPUT;
/* Make sure the values are reasonable, and try to preserve the
distance between start and end. */
if (start < 0)
start = 0;
else if (start > top_range)
start = top_range;
end = start + length;
if (end < start)
end = start;
else if (end > top_range && ! dragging)
end = top_range;
/* Store the adjusted setting in the scroll bar. */
XSETINT (bar->start, start);
XSETINT (bar->end, end);
/* Clip the end position, just for display. */
if (end > top_range)
end = top_range;
/* Draw bottom positions VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
top positions, to make sure the handle is always at least that
many pixels tall. */
end += VERTICAL_SCROLL_BAR_MIN_HANDLE;
SetControlMinimum (ch, 0);
/* Don't inadvertently activate deactivated scroll bars */
if (GetControlMaximum (ch) != -1)
SetControlMaximum (ch, top_range + VERTICAL_SCROLL_BAR_MIN_HANDLE
- (end - start));
SetControlValue (ch, start);
#if TARGET_API_MAC_CARBON
SetControlViewSize (ch, end - start);
#endif
UNBLOCK_INPUT;
}
#endif /* !USE_TOOLKIT_SCROLL_BARS */
/* Destroy scroll bar BAR, and set its Emacs window's scroll bar to
nil. */
static void
x_scroll_bar_remove (bar)
struct scroll_bar *bar;
{
FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
BLOCK_INPUT;
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
/* Destroy the Mac scroll bar control */
DisposeControl (SCROLL_BAR_CONTROL_REF (bar));
/* Disassociate this scroll bar from its window. */
XWINDOW (bar->window)->vertical_scroll_bar = Qnil;
UNBLOCK_INPUT;
}
/* Set the handle of the vertical scroll bar for WINDOW to indicate
that we are displaying PORTION characters out of a total of WHOLE
characters, starting at POSITION. If WINDOW has no scroll bar,
create one. */
static void
XTset_vertical_scroll_bar (w, portion, whole, position)
struct window *w;
int portion, whole, position;
{
struct frame *f = XFRAME (w->frame);
struct scroll_bar *bar;
int top, height, left, sb_left, width, sb_width, disp_top, disp_height;
int window_y, window_height;
#ifdef MAC_OSX
int fringe_extended_p;
#endif
/* Get window dimensions. */
window_box (w, -1, 0, &window_y, 0, &window_height);
top = window_y;
width = WINDOW_CONFIG_SCROLL_BAR_COLS (w) * FRAME_COLUMN_WIDTH (f);
height = window_height;
/* Compute the left edge of the scroll bar area. */
left = WINDOW_SCROLL_BAR_AREA_X (w);
/* Compute the width of the scroll bar which might be less than
the width of the area reserved for the scroll bar. */
if (WINDOW_CONFIG_SCROLL_BAR_WIDTH (w) > 0)
sb_width = WINDOW_CONFIG_SCROLL_BAR_WIDTH (w);
else
sb_width = width;
/* Compute the left edge of the scroll bar. */
if (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_RIGHT (w))
sb_left = left + (WINDOW_RIGHTMOST_P (w) ? width - sb_width : 0);
else
sb_left = left + (WINDOW_LEFTMOST_P (w) ? 0 : width - sb_width);
/* Adjustments according to Inside Macintosh to make it look nice */
disp_top = top;
disp_height = height;
#ifdef MAC_OS8
if (disp_top == 0)
{
disp_top = -1;
disp_height++;
}
else if (disp_top == FRAME_PIXEL_HEIGHT (f) - 16)
{
disp_top++;
disp_height--;
}
if (sb_left + sb_width == FRAME_PIXEL_WIDTH (f))
sb_left++;
#endif
#ifdef MAC_OSX
if (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (w))
fringe_extended_p = (WINDOW_LEFTMOST_P (w)
&& WINDOW_LEFT_FRINGE_WIDTH (w)
&& (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w)
|| WINDOW_LEFT_MARGIN_COLS (w) == 0));
else
fringe_extended_p = (WINDOW_RIGHTMOST_P (w)
&& WINDOW_RIGHT_FRINGE_WIDTH (w)
&& (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w)
|| WINDOW_RIGHT_MARGIN_COLS (w) == 0));
#endif
/* Does the scroll bar exist yet? */
if (NILP (w->vertical_scroll_bar))
{
BLOCK_INPUT;
#ifdef MAC_OSX
if (fringe_extended_p)
mac_clear_area (f, sb_left, top, sb_width, height);
else
#endif
mac_clear_area (f, left, top, width, height);
UNBLOCK_INPUT;
bar = x_scroll_bar_create (w, top, sb_left, sb_width, height, disp_top,
disp_height);
XSETVECTOR (w->vertical_scroll_bar, bar);
}
else
{
/* It may just need to be moved and resized. */
ControlRef ch;
bar = XSCROLL_BAR (w->vertical_scroll_bar);
ch = SCROLL_BAR_CONTROL_REF (bar);
BLOCK_INPUT;
/* If already correctly positioned, do nothing. */
if (XINT (bar->left) == sb_left
&& XINT (bar->top) == top
&& XINT (bar->width) == sb_width
&& XINT (bar->height) == height
#ifdef MAC_OSX
&& !NILP (bar->fringe_extended_p) == fringe_extended_p
#endif
)
{
if (!NILP (bar->redraw_needed_p))
{
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
Draw1Control (SCROLL_BAR_CONTROL_REF (bar));
}
}
else
{
/* Since toolkit scroll bars are smaller than the space reserved
for them on the frame, we have to clear "under" them. */
#ifdef MAC_OSX
if (fringe_extended_p)
mac_clear_area (f, sb_left, top, sb_width, height);
else
#endif
mac_clear_area (f, left, top, width, height);
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
HideControl (ch);
MoveControl (ch, sb_left + VERTICAL_SCROLL_BAR_WIDTH_TRIM, disp_top);
SizeControl (ch, sb_width - VERTICAL_SCROLL_BAR_WIDTH_TRIM * 2,
disp_height);
#ifndef USE_TOOLKIT_SCROLL_BARS
if (sb_width < disp_height)
ShowControl (ch);
#endif
/* Remember new settings. */
XSETINT (bar->left, sb_left);
XSETINT (bar->top, top);
XSETINT (bar->width, sb_width);
XSETINT (bar->height, height);
#ifdef USE_TOOLKIT_SCROLL_BARS
bar->track_top = Qnil;
bar->track_height = Qnil;
bar->min_handle = Qnil;
#endif
}
UNBLOCK_INPUT;
}
#ifdef MAC_OSX
bar->fringe_extended_p = fringe_extended_p ? Qt : Qnil;
#endif
bar->redraw_needed_p = Qnil;
#ifdef USE_TOOLKIT_SCROLL_BARS
if (NILP (bar->track_top))
{
if (sb_width >= disp_height
#ifdef MAC_OSX
|| sb_width < MAC_AQUA_SMALL_VERTICAL_SCROLL_BAR_WIDTH
#endif
)
{
XSETINT (bar->track_top, 0);
XSETINT (bar->track_height, 0);
XSETINT (bar->min_handle, 0);
}
else
{
ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
Rect r0, r1;
BLOCK_INPUT;
SetControl32BitMinimum (ch, 0);
SetControl32BitMaximum (ch, 1 << 30);
SetControlViewSize (ch, 1);
/* Move the scroll bar thumb to the top. */
SetControl32BitValue (ch, 0);
get_control_part_bounds (ch, kControlIndicatorPart, &r0);
/* Move the scroll bar thumb to the bottom. */
SetControl32BitValue (ch, 1 << 30);
get_control_part_bounds (ch, kControlIndicatorPart, &r1);
UnionRect (&r0, &r1, &r0);
XSETINT (bar->track_top, r0.top);
XSETINT (bar->track_height, r0.bottom - r0.top);
XSETINT (bar->min_handle, r1.bottom - r1.top);
/* Don't show the scroll bar if its height is not enough to
display the scroll bar thumb. */
if (r0.bottom - r0.top > 0)
ShowControl (ch);
UNBLOCK_INPUT;
}
}
x_set_toolkit_scroll_bar_thumb (bar, portion, position, whole);
#else /* not USE_TOOLKIT_SCROLL_BARS */
/* Set the scroll bar's current state, unless we're currently being
dragged. */
if (NILP (bar->dragging))
{
int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, height);
if (whole == 0)
x_scroll_bar_set_handle (bar, 0, top_range, 0);
else
{
int start = ((double) position * top_range) / whole;
int end = ((double) (position + portion) * top_range) / whole;
x_scroll_bar_set_handle (bar, start, end, 0);
}
}
#endif /* not USE_TOOLKIT_SCROLL_BARS */
}
/* The following three hooks are used when we're doing a thorough
redisplay of the frame. We don't explicitly know which scroll bars
are going to be deleted, because keeping track of when windows go
away is a real pain - "Can you say set-window-configuration, boys
and girls?" Instead, we just assert at the beginning of redisplay
that *all* scroll bars are to be removed, and then save a scroll bar
from the fiery pit when we actually redisplay its window. */
/* Arrange for all scroll bars on FRAME to be removed at the next call
to `*judge_scroll_bars_hook'. A scroll bar may be spared if
`*redeem_scroll_bar_hook' is applied to its window before the judgment. */
static void
XTcondemn_scroll_bars (frame)
FRAME_PTR frame;
{
/* Transfer all the scroll bars to FRAME_CONDEMNED_SCROLL_BARS. */
while (! NILP (FRAME_SCROLL_BARS (frame)))
{
Lisp_Object bar;
bar = FRAME_SCROLL_BARS (frame);
FRAME_SCROLL_BARS (frame) = XSCROLL_BAR (bar)->next;
XSCROLL_BAR (bar)->next = FRAME_CONDEMNED_SCROLL_BARS (frame);
XSCROLL_BAR (bar)->prev = Qnil;
if (! NILP (FRAME_CONDEMNED_SCROLL_BARS (frame)))
XSCROLL_BAR (FRAME_CONDEMNED_SCROLL_BARS (frame))->prev = bar;
FRAME_CONDEMNED_SCROLL_BARS (frame) = bar;
}
}
/* Un-mark WINDOW's scroll bar for deletion in this judgment cycle.
Note that WINDOW isn't necessarily condemned at all. */
static void
XTredeem_scroll_bar (window)
struct window *window;
{
struct scroll_bar *bar;
struct frame *f;
/* We can't redeem this window's scroll bar if it doesn't have one. */
if (NILP (window->vertical_scroll_bar))
abort ();
bar = XSCROLL_BAR (window->vertical_scroll_bar);
/* Unlink it from the condemned list. */
f = XFRAME (WINDOW_FRAME (window));
if (NILP (bar->prev))
{
/* If the prev pointer is nil, it must be the first in one of
the lists. */
if (EQ (FRAME_SCROLL_BARS (f), window->vertical_scroll_bar))
/* It's not condemned. Everything's fine. */
return;
else if (EQ (FRAME_CONDEMNED_SCROLL_BARS (f),
window->vertical_scroll_bar))
FRAME_CONDEMNED_SCROLL_BARS (f) = bar->next;
else
/* If its prev pointer is nil, it must be at the front of
one or the other! */
abort ();
}
else
XSCROLL_BAR (bar->prev)->next = bar->next;
if (! NILP (bar->next))
XSCROLL_BAR (bar->next)->prev = bar->prev;
bar->next = FRAME_SCROLL_BARS (f);
bar->prev = Qnil;
XSETVECTOR (FRAME_SCROLL_BARS (f), bar);
if (! NILP (bar->next))
XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);
}
/* Remove all scroll bars on FRAME that haven't been saved since the
last call to `*condemn_scroll_bars_hook'. */
static void
XTjudge_scroll_bars (f)
FRAME_PTR f;
{
Lisp_Object bar, next;
bar = FRAME_CONDEMNED_SCROLL_BARS (f);
/* Clear out the condemned list now so we won't try to process any
more events on the hapless scroll bars. */
FRAME_CONDEMNED_SCROLL_BARS (f) = Qnil;
for (; ! NILP (bar); bar = next)
{
struct scroll_bar *b = XSCROLL_BAR (bar);
x_scroll_bar_remove (b);
next = b->next;
b->next = b->prev = Qnil;
}
/* Now there should be no references to the condemned scroll bars,
and they should get garbage-collected. */
}
/* Handle a mouse click on the scroll bar BAR. If *EMACS_EVENT's kind
is set to something other than NO_EVENT, it is enqueued.
This may be called from a signal handler, so we have to ignore GC
mark bits. */
static void
x_scroll_bar_handle_click (bar, part_code, er, bufp)
struct scroll_bar *bar;
ControlPartCode part_code;
const EventRecord *er;
struct input_event *bufp;
{
int win_y, top_range;
if (! WINDOWP (bar->window))
abort ();
bufp->kind = SCROLL_BAR_CLICK_EVENT;
bufp->frame_or_window = bar->window;
bufp->arg = Qnil;
bar->dragging = Qnil;
switch (part_code)
{
case kControlUpButtonPart:
bufp->part = scroll_bar_up_arrow;
break;
case kControlDownButtonPart:
bufp->part = scroll_bar_down_arrow;
break;
case kControlPageUpPart:
bufp->part = scroll_bar_above_handle;
break;
case kControlPageDownPart:
bufp->part = scroll_bar_below_handle;
break;
#if TARGET_API_MAC_CARBON
default:
#else
case kControlIndicatorPart:
#endif
if (er->what == mouseDown)
bar->dragging = make_number (0);
XSETVECTOR (last_mouse_scroll_bar, bar);
bufp->part = scroll_bar_handle;
break;
}
win_y = XINT (bufp->y) - XINT (bar->top);
top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (0/*dummy*/, XINT (bar->height));
win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER;
win_y -= 24;
if (! NILP (bar->dragging))
win_y -= XINT (bar->dragging);
if (win_y < 0)
win_y = 0;
if (win_y > top_range)
win_y = top_range;
XSETINT (bufp->x, win_y);
XSETINT (bufp->y, top_range);
}
#ifndef USE_TOOLKIT_SCROLL_BARS
/* Handle some mouse motion while someone is dragging the scroll bar.
This may be called from a signal handler, so we have to ignore GC
mark bits. */
static void
x_scroll_bar_note_movement (bar, y_pos, t)
struct scroll_bar *bar;
int y_pos;
Time t;
{
FRAME_PTR f = XFRAME (XWINDOW (bar->window)->frame);
last_mouse_movement_time = t;
f->mouse_moved = 1;
XSETVECTOR (last_mouse_scroll_bar, bar);
/* If we're dragging the bar, display it. */
if (! NILP (bar->dragging))
{
/* Where should the handle be now? */
int new_start = y_pos - 24;
if (new_start != XINT (bar->start))
{
int new_end = new_start + (XINT (bar->end) - XINT (bar->start));
x_scroll_bar_set_handle (bar, new_start, new_end, 0);
}
}
}
#endif /* !USE_TOOLKIT_SCROLL_BARS */
/* Return information to the user about the current position of the mouse
on the scroll bar. */
static void
x_scroll_bar_report_motion (fp, bar_window, part, x, y, time)
FRAME_PTR *fp;
Lisp_Object *bar_window;
enum scroll_bar_part *part;
Lisp_Object *x, *y;
unsigned long *time;
{
struct scroll_bar *bar = XSCROLL_BAR (last_mouse_scroll_bar);
ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
#if TARGET_API_MAC_CARBON
WindowRef wp = GetControlOwner (ch);
#else
WindowRef wp = (*ch)->contrlOwner;
#endif
Point mouse_pos;
struct frame *f = mac_window_to_frame (wp);
int win_y, top_range;
#if TARGET_API_MAC_CARBON
GetGlobalMouse (&mouse_pos);
mouse_pos.h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
mouse_pos.v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
#else
SetPortWindowPort (wp);
GetMouse (&mouse_pos);
#endif
win_y = mouse_pos.v - XINT (bar->top);
top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height));
win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER;
win_y -= 24;
if (! NILP (bar->dragging))
win_y -= XINT (bar->dragging);
if (win_y < 0)
win_y = 0;
if (win_y > top_range)
win_y = top_range;
*fp = f;
*bar_window = bar->window;
if (! NILP (bar->dragging))
*part = scroll_bar_handle;
else if (win_y < XINT (bar->start))
*part = scroll_bar_above_handle;
else if (win_y < XINT (bar->end) + VERTICAL_SCROLL_BAR_MIN_HANDLE)
*part = scroll_bar_handle;
else
*part = scroll_bar_below_handle;
XSETINT (*x, win_y);
XSETINT (*y, top_range);
f->mouse_moved = 0;
last_mouse_scroll_bar = Qnil;
*time = last_mouse_movement_time;
}
/* The screen has been cleared so we may have changed foreground or
background colors, and the scroll bars may need to be redrawn.
Clear out the scroll bars, and ask for expose events, so we can
redraw them. */
void
x_scroll_bar_clear (f)
FRAME_PTR f;
{
Lisp_Object bar;
/* We can have scroll bars even if this is 0,
if we just turned off scroll bar mode.
But in that case we should not clear them. */
if (FRAME_HAS_VERTICAL_SCROLL_BARS (f))
for (bar = FRAME_SCROLL_BARS (f); VECTORP (bar);
bar = XSCROLL_BAR (bar)->next)
XSCROLL_BAR (bar)->redraw_needed_p = Qt;
}
/***********************************************************************
Tool-bars
***********************************************************************/
#if USE_MAC_TOOLBAR
/* In identifiers such as function/variable names, Emacs tool bar is
referred to as `tool_bar', and Carbon HIToolbar as `toolbar'. */
#define TOOLBAR_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar"))
#define TOOLBAR_ICON_ITEM_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar.icon"))
#define TOOLBAR_ITEM_COMMAND_ID_OFFSET 'Tb\0\0'
#define TOOLBAR_ITEM_COMMAND_ID_P(id) \
(((id) & ~0xffff) == TOOLBAR_ITEM_COMMAND_ID_OFFSET)
#define TOOLBAR_ITEM_COMMAND_ID_VALUE(id) \
((id) - TOOLBAR_ITEM_COMMAND_ID_OFFSET)
#define TOOLBAR_ITEM_MAKE_COMMAND_ID(value) \
((value) + TOOLBAR_ITEM_COMMAND_ID_OFFSET)
static int mac_event_to_emacs_modifiers P_ ((EventRef));
static void mac_handle_origin_change P_ ((struct frame *));
static OSStatus mac_handle_toolbar_command_event P_ ((EventHandlerCallRef,
EventRef, void *));
static void
mac_move_window_with_gravity (f, win_gravity, left, top)
struct frame *f;
int win_gravity;
short left, top;
{
Rect inner, outer;
mac_get_window_bounds (f, &inner, &outer);
switch (win_gravity)
{
case NorthWestGravity:
case WestGravity:
case SouthWestGravity:
left += inner.left - outer.left;
break;
case NorthGravity:
case CenterGravity:
case SouthGravity:
left += ((inner.left - outer.left) + (inner.right - outer.right)) / 2;
break;
case NorthEastGravity:
case EastGravity:
case SouthEastGravity:
left += inner.right - outer.right;
break;
}
switch (win_gravity)
{
case NorthWestGravity:
case NorthGravity:
case NorthEastGravity:
top += inner.top - outer.top;
break;
case WestGravity:
case CenterGravity:
case EastGravity:
top += ((inner.top - outer.top) + (inner.bottom - outer.bottom)) / 2;
break;
case SouthWestGravity:
case SouthGravity:
case SouthEastGravity:
top += inner.bottom - outer.bottom;
break;
}
MoveWindow (FRAME_MAC_WINDOW (f), left, top, false);
}
static void
mac_get_window_origin_with_gravity (f, win_gravity, left, top)
struct frame *f;
int win_gravity;
short *left, *top;
{
Rect inner, outer;
mac_get_window_bounds (f, &inner, &outer);
switch (win_gravity)
{
case NorthWestGravity:
case WestGravity:
case SouthWestGravity:
*left = outer.left;
break;
case NorthGravity:
case CenterGravity:
case SouthGravity:
*left = outer.left + ((outer.right - outer.left)
- (inner.right - inner.left)) / 2;
break;
case NorthEastGravity:
case EastGravity:
case SouthEastGravity:
*left = outer.right - (inner.right - inner.left);
break;
}
switch (win_gravity)
{
case NorthWestGravity:
case NorthGravity:
case NorthEastGravity:
*top = outer.top;
break;
case WestGravity:
case CenterGravity:
case EastGravity:
*top = outer.top + ((outer.bottom - outer.top)
- (inner.bottom - inner.top)) / 2;
break;
case SouthWestGravity:
case SouthGravity:
case SouthEastGravity:
*top = outer.bottom - (inner.bottom - inner.top);
break;
}
}
static OSStatus
mac_handle_toolbar_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus err, result = eventNotHandledErr;
switch (GetEventKind (event))
{
case kEventToolbarGetDefaultIdentifiers:
result = noErr;
break;
case kEventToolbarGetAllowedIdentifiers:
{
CFMutableArrayRef array;
GetEventParameter (event, kEventParamMutableArray,
typeCFMutableArrayRef, NULL,
sizeof (CFMutableArrayRef), NULL, &array);
CFArrayAppendValue (array, TOOLBAR_ICON_ITEM_IDENTIFIER);
result = noErr;
}
break;
case kEventToolbarCreateItemWithIdentifier:
{
CFStringRef identifier;
HIToolbarItemRef item = NULL;
GetEventParameter (event, kEventParamToolbarItemIdentifier,
typeCFStringRef, NULL,
sizeof (CFStringRef), NULL, &identifier);
if (CFStringCompare (identifier, TOOLBAR_ICON_ITEM_IDENTIFIER, 0)
== kCFCompareEqualTo)
HIToolbarItemCreate (identifier,
kHIToolbarItemAllowDuplicates
| kHIToolbarItemCantBeRemoved, &item);
if (item)
{
SetEventParameter (event, kEventParamToolbarItem,
typeHIToolbarItemRef,
sizeof (HIToolbarItemRef), &item);
result = noErr;
}
}
break;
default:
abort ();
}
return result;
}
static CGImageRef
mac_image_spec_to_cg_image (f, image)
struct frame *f;
Lisp_Object image;
{
if (!valid_image_p (image))
return NULL;
else
{
int img_id = lookup_image (f, image);
struct image *img = IMAGE_FROM_ID (f, img_id);
prepare_image_for_display (f, img);
return img->data.ptr_val;
}
}
/* Create a tool bar for frame F. */
static OSStatus
mac_create_frame_tool_bar (f)
FRAME_PTR f;
{
OSStatus err;
HIToolbarRef toolbar;
err = HIToolbarCreate (TOOLBAR_IDENTIFIER, kHIToolbarNoAttributes,
&toolbar);
if (err == noErr)
{
static const EventTypeSpec specs[] =
{{kEventClassToolbar, kEventToolbarGetDefaultIdentifiers},
{kEventClassToolbar, kEventToolbarGetAllowedIdentifiers},
{kEventClassToolbar, kEventToolbarCreateItemWithIdentifier}};
err = InstallEventHandler (HIObjectGetEventTarget (toolbar),
mac_handle_toolbar_event,
GetEventTypeCount (specs), specs,
f, NULL);
}
if (err == noErr)
err = HIToolbarSetDisplayMode (toolbar, kHIToolbarDisplayModeIconOnly);
if (err == noErr)
{
static const EventTypeSpec specs[] =
{{kEventClassCommand, kEventCommandProcess}};
err = InstallWindowEventHandler (FRAME_MAC_WINDOW (f),
mac_handle_toolbar_command_event,
GetEventTypeCount (specs),
specs, f, NULL);
}
if (err == noErr)
err = SetWindowToolbar (FRAME_MAC_WINDOW (f), toolbar);
if (toolbar)
CFRelease (toolbar);
return err;
}
/* Update the tool bar for frame F. Add new buttons and remove old. */
void
update_frame_tool_bar (f)
FRAME_PTR f;
{
HIToolbarRef toolbar = NULL;
short left, top;
CFArrayRef old_items = NULL;
CFIndex old_count;
int i, pos, win_gravity = f->output_data.mac->toolbar_win_gravity;
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
BLOCK_INPUT;
GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar);
if (toolbar == NULL)
{
mac_create_frame_tool_bar (f);
GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar);
if (toolbar == NULL)
goto out;
if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity)
mac_get_window_origin_with_gravity (f, win_gravity, &left, &top);
}
HIToolbarCopyItems (toolbar, &old_items);
if (old_items == NULL)
goto out;
old_count = CFArrayGetCount (old_items);
pos = 0;
for (i = 0; i < f->n_tool_bar_items; ++i)
{
#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
int idx;
Lisp_Object image;
CGImageRef cg_image;
CFStringRef label;
HIToolbarItemRef item;
/* If image is a vector, choose the image according to the
button state. */
image = PROP (TOOL_BAR_ITEM_IMAGES);
if (VECTORP (image))
{
if (enabled_p)
idx = (selected_p
? TOOL_BAR_IMAGE_ENABLED_SELECTED
: TOOL_BAR_IMAGE_ENABLED_DESELECTED);
else
idx = (selected_p
? TOOL_BAR_IMAGE_DISABLED_SELECTED
: TOOL_BAR_IMAGE_DISABLED_DESELECTED);
xassert (ASIZE (image) >= idx);
image = AREF (image, idx);
}
else
idx = -1;
cg_image = mac_image_spec_to_cg_image (f, image);
/* Ignore invalid image specifications. */
if (cg_image == NULL)
continue;
label = cfstring_create_with_string (PROP (TOOL_BAR_ITEM_CAPTION));
if (label == NULL)
label = CFSTR ("");
if (pos < old_count)
{
CGImageRef old_cg_image = NULL;
CFStringRef old_label = NULL;
Boolean old_enabled_p;
item = (HIToolbarItemRef) CFArrayGetValueAtIndex (old_items, pos);
HIToolbarItemCopyImage (item, &old_cg_image);
if (cg_image != old_cg_image)
HIToolbarItemSetImage (item, cg_image);
CGImageRelease (old_cg_image);
HIToolbarItemCopyLabel (item, &old_label);
if (CFStringCompare (label, old_label, 0) != kCFCompareEqualTo)
HIToolbarItemSetLabel (item, label);
CFRelease (old_label);
old_enabled_p = HIToolbarItemIsEnabled (item);
if ((enabled_p || idx >= 0) != old_enabled_p)
HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0));
}
else
{
item = NULL;
HIToolbarCreateItemWithIdentifier (toolbar,
TOOLBAR_ICON_ITEM_IDENTIFIER,
NULL, &item);
if (item)
{
HIToolbarItemSetImage (item, cg_image);
HIToolbarItemSetLabel (item, label);
HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0));
HIToolbarAppendItem (toolbar, item);
CFRelease (item);
}
}
CFRelease (label);
if (item)
{
HIToolbarItemSetCommandID (item, TOOLBAR_ITEM_MAKE_COMMAND_ID (i));
pos++;
}
}
CFRelease (old_items);
while (pos < old_count)
HIToolbarRemoveItemAtIndex (toolbar, --old_count);
ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), true,
!win_gravity && f == mac_focus_frame (dpyinfo));
/* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events on
toolbar visibility change. */
mac_handle_origin_change (f);
if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity)
{
mac_move_window_with_gravity (f, win_gravity, left, top);
/* If the title bar is completely outside the screen, adjust the
position. */
ConstrainWindowToScreen (FRAME_MAC_WINDOW (f), kWindowTitleBarRgn,
kWindowConstrainMoveRegardlessOfFit
| kWindowConstrainAllowPartial, NULL, NULL);
f->output_data.mac->toolbar_win_gravity = 0;
}
out:
UNBLOCK_INPUT;
}
/* Hide the tool bar on frame F. Unlike the counterpart on GTK+, it
doesn't deallocate the resources. */
void
free_frame_tool_bar (f)
FRAME_PTR f;
{
if (IsWindowToolbarVisible (FRAME_MAC_WINDOW (f)))
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
BLOCK_INPUT;
ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), false,
(NILP (Fsymbol_value
(intern ("frame-notice-user-settings")))
&& f == mac_focus_frame (dpyinfo)));
/* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events
on toolbar visibility change. */
mac_handle_origin_change (f);
UNBLOCK_INPUT;
}
}
static void
mac_tool_bar_note_mouse_movement (f, event)
struct frame *f;
EventRef event;
{
OSStatus err;
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
int mouse_down_p;
HIViewRef item_view;
UInt32 command_id;
mouse_down_p = (dpyinfo->grabbed
&& f == last_mouse_frame
&& FRAME_LIVE_P (f));
if (mouse_down_p)
return;
err = HIViewGetViewForMouseEvent (HIViewGetRoot (FRAME_MAC_WINDOW (f)),
event, &item_view);
/* This doesn't work on Mac OS X 10.2. On Mac OS X 10.3 and 10.4, a
toolbar item view seems to have the same command ID with that of
the toolbar item. */
if (err == noErr)
err = GetControlCommandID (item_view, &command_id);
if (err == noErr && TOOLBAR_ITEM_COMMAND_ID_P (command_id))
{
int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command_id);
if (i < f->n_tool_bar_items)
{
HIRect bounds;
HIViewRef content_view;
err = HIViewGetBounds (item_view, &bounds);
if (err == noErr)
err = HIViewFindByID (HIViewGetRoot (FRAME_MAC_WINDOW (f)),
kHIViewWindowContentID, &content_view);
if (err == noErr)
err = HIViewConvertRect (&bounds, item_view, content_view);
if (err == noErr)
SetRect (&last_mouse_glyph,
CGRectGetMinX (bounds), CGRectGetMinY (bounds),
CGRectGetMaxX (bounds), CGRectGetMaxY (bounds));
help_echo_object = help_echo_window = Qnil;
help_echo_pos = -1;
help_echo_string = PROP (TOOL_BAR_ITEM_HELP);
if (NILP (help_echo_string))
help_echo_string = PROP (TOOL_BAR_ITEM_CAPTION);
}
}
}
static OSStatus
mac_handle_toolbar_command_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus err, result = eventNotHandledErr;
struct frame *f = (struct frame *) data;
HICommand command;
err = GetEventParameter (event, kEventParamDirectObject,
typeHICommand, NULL,
sizeof (HICommand), NULL, &command);
if (err != noErr)
return result;
switch (GetEventKind (event))
{
case kEventCommandProcess:
if (!TOOLBAR_ITEM_COMMAND_ID_P (command.commandID))
result = CallNextEventHandler (next_handler, event);
else
{
int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command.commandID);
if (i < f->n_tool_bar_items
&& !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P)))
{
Lisp_Object frame;
struct input_event buf;
EVENT_INIT (buf);
XSETFRAME (frame, f);
buf.kind = TOOL_BAR_EVENT;
buf.frame_or_window = frame;
buf.arg = frame;
kbd_buffer_store_event (&buf);
buf.kind = TOOL_BAR_EVENT;
buf.frame_or_window = frame;
buf.arg = PROP (TOOL_BAR_ITEM_KEY);
buf.modifiers = mac_event_to_emacs_modifiers (event);
kbd_buffer_store_event (&buf);
result = noErr;
}
}
break;
default:
abort ();
}
#undef PROP
return result;
}
#endif /* USE_MAC_TOOLBAR */
/***********************************************************************
Text Cursor
***********************************************************************/
/* Set clipping for output in glyph row ROW. W is the window in which
we operate. GC is the graphics context to set clipping in.
ROW may be a text row or, e.g., a mode line. Text rows must be
clipped to the interior of the window dedicated to text display,
mode lines must be clipped to the whole window. */
static void
x_clip_to_row (w, row, area, gc)
struct window *w;
struct glyph_row *row;
int area;
GC gc;
{
struct frame *f = XFRAME (WINDOW_FRAME (w));
Rect clip_rect;
int window_x, window_y, window_width;
window_box (w, area, &window_x, &window_y, &window_width, 0);
clip_rect.left = window_x;
clip_rect.top = WINDOW_TO_FRAME_PIXEL_Y (w, row->y);
clip_rect.top = max (clip_rect.top, window_y);
clip_rect.right = clip_rect.left + window_width;
clip_rect.bottom = clip_rect.top + row->visible_height;
mac_set_clip_rectangles (FRAME_MAC_DISPLAY (f), gc, &clip_rect, 1);
}
/* Draw a hollow box cursor on window W in glyph row ROW. */
static void
x_draw_hollow_cursor (w, row)
struct window *w;
struct glyph_row *row;
{
struct frame *f = XFRAME (WINDOW_FRAME (w));
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
Display *dpy = FRAME_MAC_DISPLAY (f);
int x, y, wd, h;
XGCValues xgcv;
struct glyph *cursor_glyph;
GC gc;
/* Get the glyph the cursor is on. If we can't tell because
the current matrix is invalid or such, give up. */
cursor_glyph = get_phys_cursor_glyph (w);
if (cursor_glyph == NULL)
return;
/* Compute frame-relative coordinates for phys cursor. */
get_phys_cursor_geometry (w, row, cursor_glyph, &x, &y, &h);
wd = w->phys_cursor_width;
/* The foreground of cursor_gc is typically the same as the normal
background color, which can cause the cursor box to be invisible. */
xgcv.foreground = f->output_data.mac->cursor_pixel;
if (dpyinfo->scratch_cursor_gc)
XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv);
else
dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_MAC_WINDOW (f),
GCForeground, &xgcv);
gc = dpyinfo->scratch_cursor_gc;
/* Set clipping, draw the rectangle, and reset clipping again. */
x_clip_to_row (w, row, TEXT_AREA, gc);
mac_draw_rectangle (f, gc, x, y, wd, h - 1);
mac_reset_clip_rectangles (dpy, gc);
}
/* Draw a bar cursor on window W in glyph row ROW.
Implementation note: One would like to draw a bar cursor with an
angle equal to the one given by the font property XA_ITALIC_ANGLE.
Unfortunately, I didn't find a font yet that has this property set.
--gerd. */
static void
x_draw_bar_cursor (w, row, width, kind)
struct window *w;
struct glyph_row *row;
int width;
enum text_cursor_kinds kind;
{
struct frame *f = XFRAME (w->frame);
struct glyph *cursor_glyph;
/* If cursor is out of bounds, don't draw garbage. This can happen
in mini-buffer windows when switching between echo area glyphs
and mini-buffer. */
cursor_glyph = get_phys_cursor_glyph (w);
if (cursor_glyph == NULL)
return;
/* If on an image, draw like a normal cursor. That's usually better
visible than drawing a bar, esp. if the image is large so that
the bar might not be in the window. */
if (cursor_glyph->type == IMAGE_GLYPH)
{
struct glyph_row *row;
row = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos);
draw_phys_cursor_glyph (w, row, DRAW_CURSOR);
}
else
{
Display *dpy = FRAME_MAC_DISPLAY (f);
Window window = FRAME_MAC_WINDOW (f);
GC gc = FRAME_MAC_DISPLAY_INFO (f)->scratch_cursor_gc;
unsigned long mask = GCForeground | GCBackground;
struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
XGCValues xgcv;
/* If the glyph's background equals the color we normally draw
the bar cursor in, the bar cursor in its normal color is
invisible. Use the glyph's foreground color instead in this
case, on the assumption that the glyph's colors are chosen so
that the glyph is legible. */
if (face->background == f->output_data.mac->cursor_pixel)
xgcv.background = xgcv.foreground = face->foreground;
else
xgcv.background = xgcv.foreground = f->output_data.mac->cursor_pixel;
if (gc)
XChangeGC (dpy, gc, mask, &xgcv);
else
{
gc = XCreateGC (dpy, window, mask, &xgcv);
FRAME_MAC_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
}
if (width < 0)
width = FRAME_CURSOR_WIDTH (f);
width = min (cursor_glyph->pixel_width, width);
w->phys_cursor_width = width;
x_clip_to_row (w, row, TEXT_AREA, gc);
if (kind == BAR_CURSOR)
mac_fill_rectangle (f, gc,
WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x),
WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y),
width, row->height);
else
mac_fill_rectangle (f, gc,
WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x),
WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y +
row->height - width),
cursor_glyph->pixel_width,
width);
mac_reset_clip_rectangles (f, gc);
}
}
/* RIF: Define cursor CURSOR on frame F. */
static void
mac_define_frame_cursor (f, cursor)
struct frame *f;
Cursor cursor;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
if (dpyinfo->x_focus_frame == f)
SetThemeCursor (cursor);
}
/* RIF: Clear area on frame F. */
static void
mac_clear_frame_area (f, x, y, width, height)
struct frame *f;
int x, y, width, height;
{
mac_clear_area (f, x, y, width, height);
}
/* RIF: Draw cursor on window W. */
static void
mac_draw_window_cursor (w, glyph_row, x, y, cursor_type, cursor_width, on_p, active_p)
struct window *w;
struct glyph_row *glyph_row;
int x, y;
int cursor_type, cursor_width;
int on_p, active_p;
{
if (on_p)
{
w->phys_cursor_type = cursor_type;
w->phys_cursor_on_p = 1;
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);
}
else
switch (cursor_type)
{
case HOLLOW_BOX_CURSOR:
x_draw_hollow_cursor (w, glyph_row);
break;
case FILLED_BOX_CURSOR:
draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
break;
case BAR_CURSOR:
x_draw_bar_cursor (w, glyph_row, cursor_width, BAR_CURSOR);
break;
case HBAR_CURSOR:
x_draw_bar_cursor (w, glyph_row, cursor_width, HBAR_CURSOR);
break;
case NO_CURSOR:
w->phys_cursor_width = 0;
break;
default:
abort ();
}
}
}
/* Icons. */
#if 0 /* MAC_TODO: no icon support yet. */
int
x_bitmap_icon (f, icon)
struct frame *f;
Lisp_Object icon;
{
HANDLE hicon;
if (FRAME_W32_WINDOW (f) == 0)
return 1;
if (NILP (icon))
hicon = LoadIcon (hinst, EMACS_CLASS);
else if (STRINGP (icon))
hicon = LoadImage (NULL, (LPCTSTR) SDATA (icon), IMAGE_ICON, 0, 0,
LR_DEFAULTSIZE | LR_LOADFROMFILE);
else if (SYMBOLP (icon))
{
LPCTSTR name;
if (EQ (icon, intern ("application")))
name = (LPCTSTR) IDI_APPLICATION;
else if (EQ (icon, intern ("hand")))
name = (LPCTSTR) IDI_HAND;
else if (EQ (icon, intern ("question")))
name = (LPCTSTR) IDI_QUESTION;
else if (EQ (icon, intern ("exclamation")))
name = (LPCTSTR) IDI_EXCLAMATION;
else if (EQ (icon, intern ("asterisk")))
name = (LPCTSTR) IDI_ASTERISK;
else if (EQ (icon, intern ("winlogo")))
name = (LPCTSTR) IDI_WINLOGO;
else
return 1;
hicon = LoadIcon (NULL, name);
}
else
return 1;
if (hicon == NULL)
return 1;
PostMessage (FRAME_W32_WINDOW (f), WM_SETICON, (WPARAM) ICON_BIG,
(LPARAM) hicon);
return 0;
}
#endif /* MAC_TODO */
/************************************************************************
Handling X errors
************************************************************************/
/* Display Error Handling functions not used on W32. Listing them here
helps diff stay in step when comparing w32term.c with xterm.c.
x_error_catcher (display, error)
x_catch_errors (dpy)
x_catch_errors_unwind (old_val)
x_check_errors (dpy, format)
x_had_errors_p (dpy)
x_clear_errors (dpy)
x_uncatch_errors (dpy, count)
x_trace_wire ()
x_connection_signal (signalnum)
x_connection_closed (dpy, error_message)
x_error_quitter (display, error)
x_error_handler (display, error)
x_io_error_quitter (display)
*/
/* Changing the font of the frame. */
/* Give frame F the font named FONTNAME as its default font, and
return the full name of that font. FONTNAME may be a wildcard
pattern; in that case, we choose some font that fits the pattern.
The return value shows which font we chose. */
Lisp_Object
x_new_font (f, fontname)
struct frame *f;
register char *fontname;
{
struct font_info *fontp
= FS_LOAD_FONT (f, fontname);
if (!fontp)
return Qnil;
if (FRAME_FONT (f) == (XFontStruct *) (fontp->font))
/* This font is already set in frame F. There's nothing more to
do. */
return build_string (fontp->full_name);
FRAME_FONT (f) = (XFontStruct *) (fontp->font);
FRAME_BASELINE_OFFSET (f) = fontp->baseline_offset;
FRAME_FONTSET (f) = -1;
FRAME_COLUMN_WIDTH (f) = fontp->average_width;
FRAME_SPACE_WIDTH (f) = fontp->space_width;
FRAME_LINE_HEIGHT (f) = FONT_HEIGHT (FRAME_FONT (f));
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_MAC_WINDOW (f) != 0)
{
XSetFont (FRAME_MAC_DISPLAY (f), f->output_data.mac->normal_gc,
FRAME_FONT (f));
XSetFont (FRAME_MAC_DISPLAY (f), f->output_data.mac->reverse_gc,
FRAME_FONT (f));
XSetFont (FRAME_MAC_DISPLAY (f), f->output_data.mac->cursor_gc,
FRAME_FONT (f));
/* Don't change the size of a tip frame; there's no point in
doing it because it's done in Fx_show_tip, and it leads to
problems because the tip frame has no widget. */
if (NILP (tip_frame) || XFRAME (tip_frame) != f)
x_set_window_size (f, 0, FRAME_COLS (f), FRAME_LINES (f));
}
return build_string (fontp->full_name);
}
/* Give frame F the fontset named FONTSETNAME as its default fontset,
and return the full name of that fontset. FONTSETNAME may be a
wildcard pattern; in that case, we choose some fontset that fits
the pattern. FONTSETNAME may be a font name for ASCII characters;
in that case, we create a fontset from that font name.
The return value shows which fontset we chose.
If FONTSETNAME specifies the default fontset, return Qt.
If an ASCII font in the specified fontset can't be loaded, return
Qnil. */
Lisp_Object
x_new_fontset (f, fontsetname)
struct frame *f;
Lisp_Object fontsetname;
{
int fontset = fs_query_fontset (fontsetname, 0);
Lisp_Object result;
if (fontset > 0 && FRAME_FONTSET(f) == fontset)
/* This fontset is already set in frame F. There's nothing more
to do. */
return fontset_name (fontset);
else if (fontset == 0)
/* The default fontset can't be the default font. */
return Qt;
if (fontset > 0)
result = x_new_font (f, (SDATA (fontset_ascii (fontset))));
else
result = x_new_font (f, SDATA (fontsetname));
if (!STRINGP (result))
/* Can't load ASCII font. */
return Qnil;
if (fontset < 0)
fontset = new_fontset_from_font_name (result);
/* Since x_new_font doesn't update any fontset information, do it now. */
FRAME_FONTSET (f) = fontset;
return fontset_name (fontset);
}
/***********************************************************************
TODO: W32 Input Methods
***********************************************************************/
/* Listing missing functions from xterm.c helps diff stay in step.
xim_destroy_callback (xim, client_data, call_data)
xim_open_dpy (dpyinfo, resource_name)
struct xim_inst_t
xim_instantiate_callback (display, client_data, call_data)
xim_initialize (dpyinfo, resource_name)
xim_close_dpy (dpyinfo)
*/
void
mac_get_window_bounds (f, inner, outer)
struct frame *f;
Rect *inner, *outer;
{
#if TARGET_API_MAC_CARBON
GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowContentRgn, inner);
GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowStructureRgn, outer);
#else /* not TARGET_API_MAC_CARBON */
RgnHandle region = NewRgn ();
GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowContentRgn, region);
*inner = (*region)->rgnBBox;
GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowStructureRgn, region);
*outer = (*region)->rgnBBox;
DisposeRgn (region);
#endif /* not TARGET_API_MAC_CARBON */
}
static void
mac_handle_origin_change (f)
struct frame *f;
{
x_real_positions (f, &f->left_pos, &f->top_pos);
}
static void
mac_handle_size_change (f, pixelwidth, pixelheight)
struct frame *f;
int pixelwidth, pixelheight;
{
int cols, rows;
cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
if (cols != FRAME_COLS (f)
|| rows != FRAME_LINES (f)
|| pixelwidth != FRAME_PIXEL_WIDTH (f)
|| pixelheight != FRAME_PIXEL_HEIGHT (f))
{
/* We pass 1 for DELAY since we can't run Lisp code inside of
a BLOCK_INPUT. */
change_frame_size (f, rows, cols, 0, 1, 0);
FRAME_PIXEL_WIDTH (f) = pixelwidth;
FRAME_PIXEL_HEIGHT (f) = pixelheight;
/* If cursor was outside the new size, mark it as off. */
mark_window_cursors_off (XWINDOW (f->root_window));
/* Clear out any recollection of where the mouse highlighting
was, since it might be in a place that's outside the new
frame size. Actually checking whether it is outside is a
pain in the neck, so don't try--just let the highlighting be
done afresh with new size. */
cancel_mouse_face (f);
#if TARGET_API_MAC_CARBON
if (f->output_data.mac->hourglass_control)
{
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
MoveControl (f->output_data.mac->hourglass_control,
pixelwidth - HOURGLASS_WIDTH, 0);
}
#endif
}
}
/* Calculate the absolute position in frame F
from its current recorded position values and gravity. */
void
x_calc_absolute_position (f)
struct frame *f;
{
int flags = f->size_hint_flags;
Rect inner, outer;
/* We have nothing to do if the current position
is already for the top-left corner. */
if (! ((flags & XNegative) || (flags & YNegative)))
return;
/* Find the offsets of the outside upper-left corner of
the inner window, with respect to the outer window. */
BLOCK_INPUT;
mac_get_window_bounds (f, &inner, &outer);
UNBLOCK_INPUT;
/* Treat negative positions as relative to the leftmost bottommost
position that fits on the screen. */
if (flags & XNegative)
f->left_pos += (FRAME_MAC_DISPLAY_INFO (f)->width
- (outer.right - outer.left));
if (flags & YNegative)
f->top_pos += (FRAME_MAC_DISPLAY_INFO (f)->height
- (outer.bottom - outer.top));
/* The left_pos and top_pos
are now relative to the top and left screen edges,
so the flags should correspond. */
f->size_hint_flags &= ~ (XNegative | YNegative);
}
/* CHANGE_GRAVITY is 1 when calling from Fset_frame_position,
to really change the position, and 0 when calling from
x_make_frame_visible (in that case, XOFF and YOFF are the current
position values). It is -1 when calling from x_set_frame_parameters,
which means, do adjust for borders but don't change the gravity. */
void
x_set_offset (f, xoff, yoff, change_gravity)
struct frame *f;
register int xoff, yoff;
int change_gravity;
{
if (change_gravity > 0)
{
f->top_pos = yoff;
f->left_pos = xoff;
f->size_hint_flags &= ~ (XNegative | YNegative);
if (xoff < 0)
f->size_hint_flags |= XNegative;
if (yoff < 0)
f->size_hint_flags |= YNegative;
f->win_gravity = NorthWestGravity;
}
x_calc_absolute_position (f);
BLOCK_INPUT;
x_wm_set_size_hint (f, (long) 0, 0);
#if TARGET_API_MAC_CARBON
MoveWindowStructure (FRAME_MAC_WINDOW (f), f->left_pos, f->top_pos);
/* If the title bar is completely outside the screen, adjust the
position. */
ConstrainWindowToScreen (FRAME_MAC_WINDOW (f), kWindowTitleBarRgn,
kWindowConstrainMoveRegardlessOfFit
| kWindowConstrainAllowPartial, NULL, NULL);
if (!NILP (tip_frame) && XFRAME (tip_frame) == f)
mac_handle_origin_change (f);
#else
{
Rect inner, outer, screen_rect, dummy;
RgnHandle region = NewRgn ();
mac_get_window_bounds (f, &inner, &outer);
f->x_pixels_diff = inner.left - outer.left;
f->y_pixels_diff = inner.top - outer.top;
MoveWindow (FRAME_MAC_WINDOW (f), f->left_pos + f->x_pixels_diff,
f->top_pos + f->y_pixels_diff, false);
/* If the title bar is completely outside the screen, adjust the
position. The variable `outer' holds the title bar rectangle.
The variable `inner' holds slightly smaller one than `outer',
so that the calculation of overlapping may not become too
strict. */
GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowTitleBarRgn, region);
outer = (*region)->rgnBBox;
DisposeRgn (region);
inner = outer;
InsetRect (&inner, 8, 8);
screen_rect = qd.screenBits.bounds;
screen_rect.top += GetMBarHeight ();
if (!SectRect (&inner, &screen_rect, &dummy))
{
if (inner.right <= screen_rect.left)
f->left_pos = screen_rect.left;
else if (inner.left >= screen_rect.right)
f->left_pos = screen_rect.right - (outer.right - outer.left);
if (inner.bottom <= screen_rect.top)
f->top_pos = screen_rect.top;
else if (inner.top >= screen_rect.bottom)
f->top_pos = screen_rect.bottom - (outer.bottom - outer.top);
MoveWindow (FRAME_MAC_WINDOW (f), f->left_pos + f->x_pixels_diff,
f->top_pos + f->y_pixels_diff, false);
}
}
#endif
UNBLOCK_INPUT;
}
/* Call this to change the size of frame F's x-window.
If CHANGE_GRAVITY is 1, we change to top-left-corner window gravity
for this size change and subsequent size changes.
Otherwise we leave the window gravity unchanged. */
void
x_set_window_size (f, change_gravity, cols, rows)
struct frame *f;
int change_gravity;
int cols, rows;
{
int pixelwidth, pixelheight;
BLOCK_INPUT;
check_frame_size (f, &rows, &cols);
f->scroll_bar_actual_width
= FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_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);
f->win_gravity = NorthWestGravity;
x_wm_set_size_hint (f, (long) 0, 0);
SizeWindow (FRAME_MAC_WINDOW (f), pixelwidth, pixelheight, 0);
#if TARGET_API_MAC_CARBON
if (!NILP (tip_frame) && f == XFRAME (tip_frame))
#endif
mac_handle_size_change (f, pixelwidth, pixelheight);
if (f->output_data.mac->internal_border_width
!= FRAME_INTERNAL_BORDER_WIDTH (f))
{
mac_clear_window (f);
f->output_data.mac->internal_border_width
= FRAME_INTERNAL_BORDER_WIDTH (f);
}
SET_FRAME_GARBAGED (f);
UNBLOCK_INPUT;
}
/* Mouse warping. */
void x_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y);
void
x_set_mouse_position (f, x, y)
struct frame *f;
int x, y;
{
int pix_x, pix_y;
pix_x = FRAME_COL_TO_PIXEL_X (f, x) + FRAME_COLUMN_WIDTH (f) / 2;
pix_y = FRAME_LINE_TO_PIXEL_Y (f, y) + 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);
}
void
x_set_mouse_pixel_position (f, pix_x, pix_y)
struct frame *f;
int pix_x, pix_y;
{
#ifdef MAC_OSX
pix_x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
pix_y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
BLOCK_INPUT;
CGWarpMouseCursorPosition (CGPointMake (pix_x, pix_y));
UNBLOCK_INPUT;
#else
#if 0 /* MAC_TODO: LMSetMouseLocation and CursorDeviceMoveTo are non-Carbon */
BLOCK_INPUT;
XWarpPointer (FRAME_X_DISPLAY (f), None, FRAME_X_WINDOW (f),
0, 0, 0, 0, pix_x, pix_y);
UNBLOCK_INPUT;
#endif
#endif
}
/* focus shifting, raising and lowering. */
void
x_focus_on_frame (f)
struct frame *f;
{
#if 0 /* This proves to be unpleasant. */
x_raise_frame (f);
#endif
#if 0
/* I don't think that the ICCCM allows programs to do things like this
without the interaction of the window manager. Whatever you end up
doing with this code, do it to x_unfocus_frame too. */
XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
RevertToPointerRoot, CurrentTime);
#endif /* ! 0 */
}
void
x_unfocus_frame (f)
struct frame *f;
{
}
/* Raise frame F. */
void
x_raise_frame (f)
struct frame *f;
{
if (f->async_visible)
{
BLOCK_INPUT;
BringToFront (FRAME_MAC_WINDOW (f));
UNBLOCK_INPUT;
}
}
/* Lower frame F. */
void
x_lower_frame (f)
struct frame *f;
{
if (f->async_visible)
{
BLOCK_INPUT;
SendBehind (FRAME_MAC_WINDOW (f), NULL);
UNBLOCK_INPUT;
}
}
static void
XTframe_raise_lower (f, raise_flag)
FRAME_PTR f;
int raise_flag;
{
if (raise_flag)
x_raise_frame (f);
else
x_lower_frame (f);
}
/* Change of visibility. */
static void
mac_handle_visibility_change (f)
struct frame *f;
{
WindowRef wp = FRAME_MAC_WINDOW (f);
int visible = 0, iconified = 0;
struct input_event buf;
if (IsWindowVisible (wp))
{
if (IsWindowCollapsed (wp))
iconified = 1;
else
visible = 1;
}
if (!f->async_visible && visible)
{
if (f->iconified)
{
/* wait_reading_process_output will notice this and update
the frame's display structures. If we were made
invisible, we should not set garbaged, because that stops
redrawing on Update events. */
SET_FRAME_GARBAGED (f);
EVENT_INIT (buf);
buf.kind = DEICONIFY_EVENT;
XSETFRAME (buf.frame_or_window, f);
buf.arg = Qnil;
kbd_buffer_store_event (&buf);
}
else if (! NILP (Vframe_list) && ! NILP (XCDR (Vframe_list)))
/* Force a redisplay sooner or later to update the
frame titles in case this is the second frame. */
record_asynch_buffer_change ();
}
else if (f->async_visible && !visible)
if (iconified)
{
EVENT_INIT (buf);
buf.kind = ICONIFY_EVENT;
XSETFRAME (buf.frame_or_window, f);
buf.arg = Qnil;
kbd_buffer_store_event (&buf);
}
f->async_visible = visible;
f->async_iconified = iconified;
}
/* This tries to wait until the frame is really visible.
However, if the window manager asks the user where to position
the frame, this will return before the user finishes doing that.
The frame will not actually be visible at that time,
but it will become visible later when the window manager
finishes with it. */
void
x_make_frame_visible (f)
struct frame *f;
{
BLOCK_INPUT;
if (! FRAME_VISIBLE_P (f))
{
/* We test FRAME_GARBAGED_P here to make sure we don't
call x_set_offset a second time
if we get to x_make_frame_visible a second time
before the window gets really visible. */
if (! FRAME_ICONIFIED_P (f)
&& ! f->output_data.mac->asked_for_visible)
x_set_offset (f, f->left_pos, f->top_pos, 0);
f->output_data.mac->asked_for_visible = 1;
CollapseWindow (FRAME_MAC_WINDOW (f), false);
ShowWindow (FRAME_MAC_WINDOW (f));
}
XFlush (FRAME_MAC_DISPLAY (f));
/* Synchronize to ensure Emacs knows the frame is visible
before we do anything else. We do this loop with input not blocked
so that incoming events are handled. */
{
Lisp_Object frame;
int count;
/* This must come after we set COUNT. */
UNBLOCK_INPUT;
XSETFRAME (frame, f);
/* Wait until the frame is visible. Process X events until a
MapNotify event has been seen, or until we think we won't get a
MapNotify at all.. */
for (count = input_signal_count + 10;
input_signal_count < count && !FRAME_VISIBLE_P (f);)
{
/* Force processing of queued events. */
x_sync (f);
/* Machines that do polling rather than SIGIO have been
observed to go into a busy-wait here. So we'll fake an
alarm signal to let the handler know that there's something
to be read. We used to raise a real alarm, but it seems
that the handler isn't always enabled here. This is
probably a bug. */
if (input_polling_used ())
{
/* It could be confusing if a real alarm arrives while
processing the fake one. Turn it off and let the
handler reset it. */
extern void poll_for_input_1 P_ ((void));
int old_poll_suppress_count = poll_suppress_count;
poll_suppress_count = 1;
poll_for_input_1 ();
poll_suppress_count = old_poll_suppress_count;
}
/* See if a MapNotify event has been processed. */
FRAME_SAMPLE_VISIBILITY (f);
}
}
}
/* Change from mapped state to withdrawn state. */
/* Make the frame visible (mapped and not iconified). */
void
x_make_frame_invisible (f)
struct frame *f;
{
/* A deactivate event does not occur when the last visible frame is
made invisible. So if we clear the highlight here, it will not
be rehighlighted when it is made visible. */
#if 0
/* Don't keep the highlight on an invisible frame. */
if (FRAME_MAC_DISPLAY_INFO (f)->x_highlight_frame == f)
FRAME_MAC_DISPLAY_INFO (f)->x_highlight_frame = 0;
#endif
BLOCK_INPUT;
#if !TARGET_API_MAC_CARBON
/* Before unmapping the window, update the WM_SIZE_HINTS property to claim
that the current position of the window is user-specified, rather than
program-specified, so that when the window is mapped again, it will be
placed at the same location, without forcing the user to position it
by hand again (they have already done that once for this window.) */
x_wm_set_size_hint (f, (long) 0, 1);
#endif
HideWindow (FRAME_MAC_WINDOW (f));
UNBLOCK_INPUT;
#if !TARGET_API_MAC_CARBON
mac_handle_visibility_change (f);
#endif
}
/* Change window state from mapped to iconified. */
void
x_iconify_frame (f)
struct frame *f;
{
OSStatus err;
/* A deactivate event does not occur when the last visible frame is
iconified. So if we clear the highlight here, it will not be
rehighlighted when it is deiconified. */
#if 0
/* Don't keep the highlight on an invisible frame. */
if (FRAME_MAC_DISPLAY_INFO (f)->x_highlight_frame == f)
FRAME_MAC_DISPLAY_INFO (f)->x_highlight_frame = 0;
#endif
if (f->async_iconified)
return;
BLOCK_INPUT;
FRAME_SAMPLE_VISIBILITY (f);
if (! FRAME_VISIBLE_P (f))
ShowWindow (FRAME_MAC_WINDOW (f));
err = CollapseWindow (FRAME_MAC_WINDOW (f), true);
UNBLOCK_INPUT;
if (err != noErr)
error ("Can't notify window manager of iconification");
#if !TARGET_API_MAC_CARBON
mac_handle_visibility_change (f);
#endif
}
/* Free X resources of frame F. */
void
x_free_frame_resources (f)
struct frame *f;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
WindowRef wp = FRAME_MAC_WINDOW (f);
BLOCK_INPUT;
if (wp != tip_window)
remove_window_handler (wp);
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
DisposeWindow (wp);
if (wp == tip_window)
/* Neither WaitNextEvent nor ReceiveNextEvent receives `window
closed' event. So we reset tip_window here. */
tip_window = NULL;
free_frame_menubar (f);
if (FRAME_FACE_CACHE (f))
free_frame_faces (f);
x_free_gcs (f);
if (FRAME_SIZE_HINTS (f))
xfree (FRAME_SIZE_HINTS (f));
xfree (f->output_data.mac);
f->output_data.mac = NULL;
if (f == dpyinfo->x_focus_frame)
{
dpyinfo->x_focus_frame = 0;
#if USE_MAC_FONT_PANEL
mac_set_font_info_for_selection (NULL, DEFAULT_FACE_ID, 0);
#endif
}
if (f == dpyinfo->x_focus_event_frame)
dpyinfo->x_focus_event_frame = 0;
if (f == dpyinfo->x_highlight_frame)
dpyinfo->x_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;
}
UNBLOCK_INPUT;
}
/* Destroy the X window of frame F. */
void
x_destroy_window (f)
struct frame *f;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
x_free_frame_resources (f);
dpyinfo->reference_count--;
}
/* Setting window manager hints. */
/* Set the normal size hints for the window manager, for frame F.
FLAGS is the flags word to use--or 0 meaning preserve the flags
that the window now has.
If USER_POSITION is nonzero, we set the USPosition
flag (this is useful when FLAGS is 0). */
void
x_wm_set_size_hint (f, flags, user_position)
struct frame *f;
long flags;
int user_position;
{
int base_width, base_height, width_inc, height_inc;
int min_rows = 0, min_cols = 0;
XSizeHints *size_hints;
base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0);
width_inc = FRAME_COLUMN_WIDTH (f);
height_inc = FRAME_LINE_HEIGHT (f);
check_frame_size (f, &min_rows, &min_cols);
size_hints = FRAME_SIZE_HINTS (f);
if (size_hints == NULL)
{
size_hints = FRAME_SIZE_HINTS (f) = xmalloc (sizeof (XSizeHints));
bzero (size_hints, sizeof (XSizeHints));
}
size_hints->flags |= PResizeInc | PMinSize | PBaseSize ;
size_hints->width_inc = width_inc;
size_hints->height_inc = height_inc;
size_hints->min_width = base_width + min_cols * width_inc;
size_hints->min_height = base_height + min_rows * height_inc;
size_hints->base_width = base_width;
size_hints->base_height = base_height;
if (flags)
size_hints->flags = flags;
else if (user_position)
{
size_hints->flags &= ~ PPosition;
size_hints->flags |= USPosition;
}
}
#if 0 /* MAC_TODO: hide application instead of iconify? */
/* Used for IconicState or NormalState */
void
x_wm_set_window_state (f, state)
struct frame *f;
int state;
{
#ifdef USE_X_TOOLKIT
Arg al[1];
XtSetArg (al[0], XtNinitialState, state);
XtSetValues (f->output_data.x->widget, al, 1);
#else /* not USE_X_TOOLKIT */
Window window = FRAME_X_WINDOW (f);
f->output_data.x->wm_hints.flags |= StateHint;
f->output_data.x->wm_hints.initial_state = state;
XSetWMHints (FRAME_X_DISPLAY (f), window, &f->output_data.x->wm_hints);
#endif /* not USE_X_TOOLKIT */
}
void
x_wm_set_icon_pixmap (f, pixmap_id)
struct frame *f;
int pixmap_id;
{
Pixmap icon_pixmap;
#ifndef USE_X_TOOLKIT
Window window = FRAME_X_WINDOW (f);
#endif
if (pixmap_id > 0)
{
icon_pixmap = x_bitmap_pixmap (f, pixmap_id);
f->output_data.x->wm_hints.icon_pixmap = icon_pixmap;
}
else
{
/* It seems there is no way to turn off use of an icon pixmap.
The following line does it, only if no icon has yet been created,
for some window managers. But with mwm it crashes.
Some people say it should clear the IconPixmapHint bit in this case,
but that doesn't work, and the X consortium said it isn't the
right thing at all. Since there is no way to win,
best to explicitly give up. */
#if 0
f->output_data.x->wm_hints.icon_pixmap = None;
#else
return;
#endif
}
#ifdef USE_X_TOOLKIT /* same as in x_wm_set_window_state. */
{
Arg al[1];
XtSetArg (al[0], XtNiconPixmap, icon_pixmap);
XtSetValues (f->output_data.x->widget, al, 1);
}
#else /* not USE_X_TOOLKIT */
f->output_data.x->wm_hints.flags |= IconPixmapHint;
XSetWMHints (FRAME_X_DISPLAY (f), window, &f->output_data.x->wm_hints);
#endif /* not USE_X_TOOLKIT */
}
#endif /* MAC_TODO */
void
x_wm_set_icon_position (f, icon_x, icon_y)
struct frame *f;
int icon_x, icon_y;
{
#if 0 /* MAC_TODO: no icons on Mac */
#ifdef USE_X_TOOLKIT
Window window = XtWindow (f->output_data.x->widget);
#else
Window window = FRAME_X_WINDOW (f);
#endif
f->output_data.x->wm_hints.flags |= IconPositionHint;
f->output_data.x->wm_hints.icon_x = icon_x;
f->output_data.x->wm_hints.icon_y = icon_y;
XSetWMHints (FRAME_X_DISPLAY (f), window, &f->output_data.x->wm_hints);
#endif /* MAC_TODO */
}
/***********************************************************************
XLFD Pattern Match
***********************************************************************/
/* An XLFD pattern is divided into blocks delimited by '*'. This
structure holds information for each block. */
struct xlfdpat_block
{
/* Length of the pattern string in this block. Non-zero except for
the first and the last blocks. */
int len;
/* Pattern string except the last character in this block. The last
character is replaced with NUL in order to use it as a
sentinel. */
unsigned char *pattern;
/* Last character of the pattern string. Must not be '?'. */
unsigned char last_char;
/* One of the tables for the Boyer-Moore string search. It
specifies the number of positions to proceed for each character
with which the match fails. */
int skip[256];
/* The skip value for the last character in the above `skip' is
assigned to `infinity' in order to simplify a loop condition.
The original value is saved here. */
int last_char_skip;
};
struct xlfdpat
{
/* Normalized pattern string. "Normalized" means that capital
letters are lowered, blocks are not empty except the first and
the last ones, and trailing '?'s in a block that is not the last
one are moved to the next one. The last character in each block
is replaced with NUL. */
unsigned char *buf;
/* Number of characters except '*'s and trailing '?'s in the
normalized pattern string. */
int nchars;
/* Number of trailing '?'s in the normalized pattern string. */
int trailing_anychars;
/* Number of blocks and information for each block. The latter is
NULL if the pattern is exact (no '*' or '?' in it). */
int nblocks;
struct xlfdpat_block *blocks;
};
static void
xlfdpat_destroy (pat)
struct xlfdpat *pat;
{
if (pat)
{
if (pat->buf)
{
if (pat->blocks)
xfree (pat->blocks);
xfree (pat->buf);
}
xfree (pat);
}
}
static struct xlfdpat *
xlfdpat_create (pattern)
const char *pattern;
{
struct xlfdpat *pat;
int nblocks, i, skip;
unsigned char last_char, *p, *q, *anychar_head;
const unsigned char *ptr;
struct xlfdpat_block *blk;
pat = xmalloc (sizeof (struct xlfdpat));
pat->buf = xmalloc (strlen (pattern) + 1);
/* Normalize the pattern string and store it to `pat->buf'. */
nblocks = 0;
anychar_head = NULL;
q = pat->buf;
last_char = '\0';
for (ptr = pattern; *ptr; ptr++)
{
unsigned char c = *ptr;
if (c == '*')
if (last_char == '*')
/* ...a** -> ...a* */
continue;
else
{
if (last_char == '?')
{
if (anychar_head > pat->buf && *(anychar_head - 1) == '*')
/* ...*??* -> ...*?? */
continue;
else
/* ...a??* -> ...a*?? */
{
*anychar_head++ = '*';
c = '?';
}
}
nblocks++;
}
else if (c == '?')
{
if (last_char != '?')
anychar_head = q;
}
else
/* On Mac OS X 10.3, tolower also converts non-ASCII
characters for some locales. */
if (isascii (c))
c = tolower (c);
*q++ = last_char = c;
}
*q = '\0';
nblocks++;
pat->nblocks = nblocks;
if (last_char != '?')
pat->trailing_anychars = 0;
else
{
pat->trailing_anychars = q - anychar_head;
q = anychar_head;
}
pat->nchars = q - pat->buf - (nblocks - 1);
if (anychar_head == NULL && nblocks == 1)
{
/* The pattern is exact. */
pat->blocks = NULL;
return pat;
}
pat->blocks = xmalloc (sizeof (struct xlfdpat_block) * nblocks);
/* Divide the normalized pattern into blocks. */
p = pat->buf;
for (blk = pat->blocks; blk < pat->blocks + nblocks - 1; blk++)
{
blk->pattern = p;
while (*p != '*')
p++;
blk->len = p - blk->pattern;
p++;
}
blk->pattern = p;
blk->len = q - blk->pattern;
/* Setup a table for the Boyer-Moore string search. */
for (blk = pat->blocks; blk < pat->blocks + nblocks; blk++)
if (blk->len != 0)
{
blk->last_char = blk->pattern[blk->len - 1];
blk->pattern[blk->len - 1] = '\0';
for (skip = 1; skip < blk->len; skip++)
if (blk->pattern[blk->len - skip - 1] == '?')
break;
for (i = 0; i < 256; i++)
blk->skip[i] = skip;
p = blk->pattern + (blk->len - skip);
while (--skip > 0)
blk->skip[*p++] = skip;
blk->last_char_skip = blk->skip[blk->last_char];
}
return pat;
}
static INLINE int
xlfdpat_exact_p (pat)
struct xlfdpat *pat;
{
return pat->blocks == NULL;
}
/* Return the first string in STRING + 0, ..., STRING + START_MAX such
that the pattern in *BLK matches with its prefix. Return NULL
there is no such strings. STRING must be lowered in advance. */
static const char *
xlfdpat_block_match_1 (blk, string, start_max)
struct xlfdpat_block *blk;
const unsigned char *string;
int start_max;
{
int start, infinity;
unsigned char *p;
const unsigned char *s;
xassert (blk->len > 0);
xassert (start_max + blk->len <= strlen (string));
xassert (blk->last_char != '?');
/* See the comments in the function `boyer_moore' (search.c) for the
use of `infinity'. */
infinity = start_max + blk->len + 1;
blk->skip[blk->last_char] = infinity;
start = 0;
do
{
/* Check the last character of the pattern. */
s = string + blk->len - 1;
do
{
start += blk->skip[*(s + start)];
}
while (start <= start_max);
if (start < infinity)
/* Couldn't find the last character. */
return NULL;
/* No less than `infinity' means we could find the last
character at `s[start - infinity]'. */
start -= infinity;
/* Check the remaining characters. We prefer making no-'?'
cases faster because the use of '?' is really rare. */
p = blk->pattern;
s = string + start;
do
{
while (*p++ == *s++)
;
}
while (*(p - 1) == '?');
if (*(p - 1) == '\0')
/* Matched. */
return string + start;
/* Didn't match. */
start += blk->last_char_skip;
}
while (start <= start_max);
return NULL;
}
#define xlfdpat_block_match(b, s, m) \
((b)->len == 1 ? memchr ((s), (b)->last_char, (m) + 1) \
: xlfdpat_block_match_1 (b, s, m))
/* Check if XLFD pattern PAT, which is generated by `xlfdpat_create',
matches with STRING. STRING must be lowered in advance. */
static int
xlfdpat_match (pat, string)
struct xlfdpat *pat;
const unsigned char *string;
{
int str_len, nblocks, i, start_max;
struct xlfdpat_block *blk;
const unsigned char *s;
xassert (pat->nblocks > 0);
if (xlfdpat_exact_p (pat))
return strcmp (pat->buf, string) == 0;
/* The number of the characters in the string must not be smaller
than that in the pattern. */
str_len = strlen (string);
if (str_len < pat->nchars + pat->trailing_anychars)
return 0;
/* Chop off the trailing '?'s. */
str_len -= pat->trailing_anychars;
/* The last block. When it is non-empty, it must match at the end
of the string. */
nblocks = pat->nblocks;
blk = pat->blocks + (nblocks - 1);
if (nblocks == 1)
/* The last block is also the first one. */
return (str_len == blk->len
&& (blk->len == 0 || xlfdpat_block_match (blk, string, 0)));
else if (blk->len != 0)
if (!xlfdpat_block_match (blk, string + (str_len - blk->len), 0))
return 0;
/* The first block. When it is non-empty, it must match at the
beginning of the string. */
blk = pat->blocks;
if (blk->len != 0)
{
s = xlfdpat_block_match (blk, string, 0);
if (s == NULL)
return 0;
string = s + blk->len;
}
/* The rest of the blocks. */
start_max = str_len - pat->nchars;
for (i = 1, blk++; i < nblocks - 1; i++, blk++)
{
s = xlfdpat_block_match (blk, string, start_max);
if (s == NULL)
return 0;
start_max -= s - string;
string = s + blk->len;
}
return 1;
}
/***********************************************************************
Fonts
***********************************************************************/
/* Return a pointer to struct font_info of font FONT_IDX of frame F. */
struct font_info *
x_get_font_info (f, font_idx)
FRAME_PTR f;
int font_idx;
{
return (FRAME_MAC_FONT_TABLE (f) + font_idx);
}
/* the global font name table */
static char **font_name_table = NULL;
static int font_name_table_size = 0;
static int font_name_count = 0;
/* Alist linking font family names to Font Manager font family
references (which can also be used as QuickDraw font IDs). We use
an alist because hash tables are not ready when the terminal frame
for Mac OS Classic is created. */
static Lisp_Object fm_font_family_alist;
#if USE_ATSUI
/* Hash table linking font family names to ATSU font IDs. */
static Lisp_Object atsu_font_id_hash;
/* Alist linking Font Manager style to face attributes. */
static Lisp_Object fm_style_face_attributes_alist;
extern Lisp_Object QCfamily, QCweight, QCslant, Qnormal, Qbold, Qitalic;
#endif
/* Alist linking character set strings to Mac text encoding and Emacs
coding system. */
static Lisp_Object Vmac_charset_info_alist;
static Lisp_Object
create_text_encoding_info_alist ()
{
Lisp_Object result = Qnil, rest;
for (rest = Vmac_charset_info_alist; CONSP (rest); rest = XCDR (rest))
{
Lisp_Object charset_info = XCAR (rest);
Lisp_Object charset, coding_system, text_encoding;
Lisp_Object existing_info;
if (!(CONSP (charset_info)
&& (charset = XCAR (charset_info),
STRINGP (charset))
&& CONSP (XCDR (charset_info))
&& (text_encoding = XCAR (XCDR (charset_info)),
INTEGERP (text_encoding))
&& CONSP (XCDR (XCDR (charset_info)))
&& (coding_system = XCAR (XCDR (XCDR (charset_info))),
SYMBOLP (coding_system))))
continue;
existing_info = assq_no_quit (text_encoding, result);
if (NILP (existing_info))
result = Fcons (list3 (text_encoding, coding_system, charset),
result);
else
if (NILP (Fmember (charset, XCDR (XCDR (existing_info)))))
XSETCDR (XCDR (existing_info),
Fcons (charset, XCDR (XCDR (existing_info))));
}
return result;
}
static void
decode_mac_font_name (name, size, coding_system)
char *name;
int size;
Lisp_Object coding_system;
{
struct coding_system coding;
char *buf, *p;
if (!NILP (coding_system) && !NILP (Fcoding_system_p (coding_system)))
{
for (p = name; *p; p++)
if (!isascii (*p) || iscntrl (*p))
break;
if (*p)
{
setup_coding_system (coding_system, &coding);
coding.src_multibyte = 0;
coding.dst_multibyte = 1;
coding.mode |= CODING_MODE_LAST_BLOCK;
coding.dst_bytes = size;
coding.destination = (unsigned char *) alloca (coding.dst_bytes);
decode_coding_c_string (&coding, name, strlen (name), Qnil);
bcopy (coding.destination, name, min (coding.produced, size));
name[min (coding.produced, size)] = '\0';
}
}
/* If there's just one occurrence of '-' in the family name, it is
replaced with '_'. (More than one occurrence of '-' means a
"FOUNDRY-FAMILY-CHARSET"-style name.) */
p = strchr (name, '-');
if (p && strchr (p + 1, '-') == NULL)
*p = '_';
for (p = name; *p; p++)
/* On Mac OS X 10.3, tolower also converts non-ASCII characters
for some locales. */
if (isascii (*p))
*p = tolower (*p);
}
static char *
mac_to_x_fontname (name, size, style, charset)
const char *name;
int size;
Style style;
char *charset;
{
Str31 foundry, cs;
Str255 family;
char xf[256], *result;
unsigned char *p;
if (sscanf (name, "%31[^-]-%255[^-]-%31s", foundry, family, cs) == 3)
charset = cs;
else
{
strcpy(foundry, "Apple");
strcpy(family, name);
}
sprintf (xf, "%s-%c-normal--%d-%d-%d-%d-m-%d-%s",
style & bold ? "bold" : "medium", style & italic ? 'i' : 'r',
size, size * 10, size ? 72 : 0, size ? 72 : 0, size * 10, charset);
result = xmalloc (strlen (foundry) + strlen (family) + strlen (xf) + 3 + 1);
sprintf (result, "-%s-%s-%s", foundry, family, xf);
for (p = result; *p; p++)
/* On Mac OS X 10.3, tolower also converts non-ASCII characters
for some locales. */
if (isascii (*p))
*p = tolower (*p);
return result;
}
/* Parse fully-specified and instantiated X11 font spec XF, and store
the results to FAMILY, *SIZE, *STYLE, and CHARSET. Return 1 if the
parsing succeeded, and 0 otherwise. For FAMILY and CHARSET, the
caller must allocate at least 256 and 32 bytes respectively. For
ordinary Mac fonts, the value stored to FAMILY should just be their
names, like "monaco", "Taipei", etc. Fonts converted from the GNU
intlfonts collection contain their charset designation in their
names, like "ETL-Fixed-iso8859-1", "ETL-Fixed-koi8-r", etc. Both
types of font names are handled accordingly. */
const int kDefaultFontSize = 12;
static int
parse_x_font_name (xf, family, size, style, charset)
const char *xf;
char *family;
int *size;
Style *style;
char *charset;
{
Str31 foundry, weight;
int point_size, avgwidth;
char slant[2], *p;
if (sscanf (xf, "-%31[^-]-%255[^-]-%31[^-]-%1[^-]-%*[^-]-%*[^-]-%d-%d-%*[^-]-%*[^-]-%*c-%d-%31s",
foundry, family, weight, slant, size,
&point_size, &avgwidth, charset) != 8
&& sscanf (xf, "-%31[^-]-%255[^-]-%31[^-]-%1[^-]-%*[^-]--%d-%d-%*[^-]-%*[^-]-%*c-%d-%31s",
foundry, family, weight, slant, size,
&point_size, &avgwidth, charset) != 8)
return 0;
if (*size == 0)
{
if (point_size > 0)
*size = point_size / 10;
else if (avgwidth > 0)
*size = avgwidth / 10;
}
if (*size == 0)
*size = kDefaultFontSize;
*style = normal;
if (strcmp (weight, "bold") == 0)
*style |= bold;
if (*slant == 'i')
*style |= italic;
if (NILP (Fassoc (build_string (charset), Vmac_charset_info_alist)))
{
int foundry_len = strlen (foundry), family_len = strlen (family);
if (foundry_len + family_len + strlen (charset) + 2 < sizeof (Str255))
{
/* Like sprintf (family, "%s-%s-%s", foundry, family, charset),
but take overlap into account. */
memmove (family + foundry_len + 1, family, family_len);
memcpy (family, foundry, foundry_len);
family[foundry_len] = '-';
family[foundry_len + 1 + family_len] = '-';
strcpy (family + foundry_len + 1 + family_len + 1, charset);
}
else
return 0;
}
for (p = family; *p; p++)
/* On Mac OS X 10.3, tolower also converts non-ASCII characters
for some locales. */
if (isascii (*p))
*p = tolower (*p);
return 1;
}
static void
add_font_name_table_entry (char *font_name)
{
if (font_name_table_size == 0)
{
font_name_table_size = 256;
font_name_table = (char **)
xmalloc (font_name_table_size * sizeof (char *));
}
else if (font_name_count + 1 >= font_name_table_size)
{
font_name_table_size *= 2;
font_name_table = (char **)
xrealloc (font_name_table,
font_name_table_size * sizeof (char *));
}
font_name_table[font_name_count++] = font_name;
}
static void
add_mac_font_name (name, size, style, charset)
const char *name;
int size;
Style style;
const char *charset;
{
if (size > 0)
add_font_name_table_entry (mac_to_x_fontname (name, size, style, charset));
else
{
add_font_name_table_entry (mac_to_x_fontname (name, 0, style, charset));
add_font_name_table_entry (mac_to_x_fontname (name, 0, italic, charset));
add_font_name_table_entry (mac_to_x_fontname (name, 0, bold, charset));
add_font_name_table_entry (mac_to_x_fontname (name, 0, italic | bold,
charset));
}
}
#if USE_ATSUI
static FMFontStyle
fm_get_style_from_font (font)
FMFont font;
{
OSStatus err;
FMFontStyle style = normal;
ByteCount len;
UInt16 mac_style;
FMFontFamily font_family;
#define FONT_HEADER_MAC_STYLE_OFFSET (4*4 + 2*2 + 8*2 + 2*4)
/* FMGetFontFamilyInstanceFromFont returns `normal' as the style of
some font (e.g., Optima) even if it is `bold'. */
err = FMGetFontTable (font, 'head', FONT_HEADER_MAC_STYLE_OFFSET,
sizeof (mac_style), &mac_style, &len);
if (err == noErr
&& len >= FONT_HEADER_MAC_STYLE_OFFSET + sizeof (mac_style))
style = EndianU16_BtoN (mac_style);
else
FMGetFontFamilyInstanceFromFont (font, &font_family, &style);
return style;
}
static ATSUFontID
atsu_find_font_from_family_name (family)
const char *family;
{
struct Lisp_Hash_Table *h = XHASH_TABLE (atsu_font_id_hash);
unsigned hash_code;
int i;
Lisp_Object rest, best;
FMFontStyle min_style, style;
i = hash_lookup (h, make_unibyte_string (family, strlen (family)),
&hash_code);
if (i < 0)
return kATSUInvalidFontID;
rest = HASH_VALUE (h, i);
if (INTEGERP (rest) || (CONSP (rest) && INTEGERP (XCDR (rest))))
return cons_to_long (rest);
rest = Fnreverse (rest);
best = XCAR (rest);
rest = XCDR (rest);
if (!NILP (rest)
&& (min_style = fm_get_style_from_font (cons_to_long (best))) != normal)
do
{
style = fm_get_style_from_font (cons_to_long (XCAR (rest)));
if (style < min_style)
{
best = XCAR (rest);
if (style == normal)
break;
else
min_style = style;
}
rest = XCDR (rest);
}
while (!NILP (rest));
HASH_VALUE (h, i) = best;
return cons_to_long (best);
}
static Lisp_Object
fm_style_to_face_attributes (fm_style)
FMFontStyle fm_style;
{
Lisp_Object tem;
fm_style &= (bold | italic);
tem = assq_no_quit (make_number (fm_style),
fm_style_face_attributes_alist);
if (!NILP (tem))
return XCDR (tem);
tem = list4 (QCweight, fm_style & bold ? Qbold : Qnormal,
QCslant, fm_style & italic ? Qitalic : Qnormal);
fm_style_face_attributes_alist =
Fcons (Fcons (make_number (fm_style), tem),
fm_style_face_attributes_alist);
return tem;
}
static Lisp_Object
atsu_find_font_family_name (font_id)
ATSUFontID font_id;
{
OSStatus err;
ByteCount len;
Lisp_Object family = Qnil;
err = ATSUFindFontName (font_id, kFontFamilyName,
kFontMacintoshPlatform, kFontNoScript,
kFontNoLanguage, 0, NULL, &len, NULL);
if (err == noErr)
{
family = make_uninit_string (len);
err = ATSUFindFontName (font_id, kFontFamilyName,
kFontMacintoshPlatform, kFontNoScript,
kFontNoLanguage, len, SDATA (family),
NULL, NULL);
}
if (err == noErr)
decode_mac_font_name (SDATA (family), len + 1, Qnil);
return family;
}
Lisp_Object
mac_atsu_font_face_attributes (font_id)
ATSUFontID font_id;
{
Lisp_Object family, style_attrs;
family = atsu_find_font_family_name (font_id);
if (NILP (family))
return Qnil;
style_attrs = fm_style_to_face_attributes (fm_get_style_from_font (font_id));
return Fcons (QCfamily, Fcons (family, style_attrs));
}
#endif
/* Sets up the table font_name_table to contain the list of all fonts
in the system the first time the table is used so that the Resource
Manager need not be accessed every time this information is
needed. */
static void
init_font_name_table ()
{
#if TARGET_API_MAC_CARBON
FMFontFamilyIterator ffi;
FMFontFamilyInstanceIterator ffii;
FMFontFamily ff;
Lisp_Object text_encoding_info_alist;
struct gcpro gcpro1;
text_encoding_info_alist = create_text_encoding_info_alist ();
#if USE_ATSUI
#if USE_CG_TEXT_DRAWING
init_cg_text_anti_aliasing_threshold ();
#endif
if (!NILP (assq_no_quit (make_number (kTextEncodingMacUnicode),
text_encoding_info_alist)))
{
OSStatus err;
struct Lisp_Hash_Table *h;
unsigned hash_code;
ItemCount nfonts, i;
ATSUFontID *font_ids = NULL;
Lisp_Object prev_family = Qnil;
int j;
atsu_font_id_hash =
make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
make_float (DEFAULT_REHASH_SIZE),
make_float (DEFAULT_REHASH_THRESHOLD),
Qnil, Qnil, Qnil);
h = XHASH_TABLE (atsu_font_id_hash);
err = ATSUFontCount (&nfonts);
if (err == noErr)
{
font_ids = xmalloc (sizeof (ATSUFontID) * nfonts);
err = ATSUGetFontIDs (font_ids, nfonts, NULL);
}
if (err == noErr)
for (i = 0; i < nfonts; i++)
{
Lisp_Object family;
family = atsu_find_font_family_name (font_ids[i]);
if (NILP (family) || SREF (family, 0) == '.')
continue;
if (!NILP (Fequal (prev_family, family)))
family = prev_family;
else
j = hash_lookup (h, family, &hash_code);
if (j < 0)
{
add_mac_font_name (SDATA (family), 0, normal, "iso10646-1");
j = hash_put (h, family, Fcons (long_to_cons (font_ids[i]),
Qnil), hash_code);
}
else if (EQ (prev_family, family))
HASH_VALUE (h, j) = Fcons (long_to_cons (font_ids[i]),
HASH_VALUE (h, j));
prev_family = family;
}
if (font_ids)
xfree (font_ids);
}
#endif
/* Create a dummy instance iterator here to avoid creating and
destroying it in the loop. */
if (FMCreateFontFamilyInstanceIterator (0, &ffii) != noErr)
return;
/* Create an iterator to enumerate the font families. */
if (FMCreateFontFamilyIterator (NULL, NULL, kFMDefaultOptions, &ffi)
!= noErr)
{
FMDisposeFontFamilyInstanceIterator (&ffii);
return;
}
GCPRO1 (text_encoding_info_alist);
while (FMGetNextFontFamily (&ffi, &ff) == noErr)
{
Str255 name;
FMFont font;
FMFontStyle style;
FMFontSize size;
TextEncoding encoding;
TextEncodingBase sc;
Lisp_Object text_encoding_info, family;
if (FMGetFontFamilyName (ff, name) != noErr)
continue;
p2cstr (name);
if (*name == '.')
continue;
if (FMGetFontFamilyTextEncoding (ff, &encoding) != noErr)
continue;
sc = GetTextEncodingBase (encoding);
text_encoding_info = assq_no_quit (make_number (sc),
text_encoding_info_alist);
if (NILP (text_encoding_info))
text_encoding_info = assq_no_quit (make_number (kTextEncodingMacRoman),
text_encoding_info_alist);
decode_mac_font_name (name, sizeof (name),
XCAR (XCDR (text_encoding_info)));
family = build_string (name);
if (!NILP (Fassoc (family, fm_font_family_alist)))
continue;
fm_font_family_alist = Fcons (Fcons (family, make_number (ff)),
fm_font_family_alist);
/* Point the instance iterator at the current font family. */
if (FMResetFontFamilyInstanceIterator (ff, &ffii) != noErr)
continue;
while (FMGetNextFontFamilyInstance (&ffii, &font, &style, &size)
== noErr)
{
Lisp_Object rest = XCDR (XCDR (text_encoding_info));
if (size > 0 || style == normal)
for (; CONSP (rest); rest = XCDR (rest))
add_mac_font_name (name, size, style, SDATA (XCAR (rest)));
}
}
UNGCPRO;
/* Dispose of the iterators. */
FMDisposeFontFamilyIterator (&ffi);
FMDisposeFontFamilyInstanceIterator (&ffii);
#else /* !TARGET_API_MAC_CARBON */
GrafPtr port;
SInt16 fontnum, old_fontnum;
int num_mac_fonts = CountResources('FOND');
int i, j;
Handle font_handle, font_handle_2;
short id, scriptcode;
ResType type;
Str255 name;
struct FontAssoc *fat;
struct AsscEntry *assc_entry;
Lisp_Object text_encoding_info_alist, text_encoding_info, family;
struct gcpro gcpro1;
GetPort (&port); /* save the current font number used */
old_fontnum = port->txFont;
text_encoding_info_alist = create_text_encoding_info_alist ();
GCPRO1 (text_encoding_info_alist);
for (i = 1; i <= num_mac_fonts; i++) /* get all available fonts */
{
font_handle = GetIndResource ('FOND', i);
if (!font_handle)
continue;
GetResInfo (font_handle, &id, &type, name);
GetFNum (name, &fontnum);
p2cstr (name);
if (fontnum == 0 || *name == '.')
continue;
TextFont (fontnum);
scriptcode = FontToScript (fontnum);
text_encoding_info = assq_no_quit (make_number (scriptcode),
text_encoding_info_alist);
if (NILP (text_encoding_info))
text_encoding_info = assq_no_quit (make_number (smRoman),
text_encoding_info_alist);
decode_mac_font_name (name, sizeof (name),
XCAR (XCDR (text_encoding_info)));
family = build_string (name);
if (!NILP (Fassoc (family, fm_font_family_alist)))
continue;
fm_font_family_alist = Fcons (Fcons (family, make_number (fontnum)),
fm_font_family_alist);
do
{
HLock (font_handle);
if (GetResourceSizeOnDisk (font_handle)
>= sizeof (struct FamRec))
{
fat = (struct FontAssoc *) (*font_handle
+ sizeof (struct FamRec));
assc_entry
= (struct AsscEntry *) (*font_handle
+ sizeof (struct FamRec)
+ sizeof (struct FontAssoc));
for (j = 0; j <= fat->numAssoc; j++, assc_entry++)
{
Lisp_Object rest = XCDR (XCDR (text_encoding_info));
for (; CONSP (rest); rest = XCDR (rest))
add_mac_font_name (name, assc_entry->fontSize,
assc_entry->fontStyle,
SDATA (XCAR (rest)));
}
}
HUnlock (font_handle);
font_handle_2 = GetNextFOND (font_handle);
ReleaseResource (font_handle);
font_handle = font_handle_2;
}
while (ResError () == noErr && font_handle);
}
UNGCPRO;
TextFont (old_fontnum);
#endif /* !TARGET_API_MAC_CARBON */
}
void
mac_clear_font_name_table ()
{
int i;
for (i = 0; i < font_name_count; i++)
xfree (font_name_table[i]);
xfree (font_name_table);
font_name_table = NULL;
font_name_table_size = font_name_count = 0;
fm_font_family_alist = Qnil;
}
enum xlfd_scalable_field_index
{
XLFD_SCL_PIXEL_SIZE,
XLFD_SCL_POINT_SIZE,
XLFD_SCL_AVGWIDTH,
XLFD_SCL_LAST
};
static const int xlfd_scalable_fields[] =
{
6, /* PIXEL_SIZE */
7, /* POINT_SIZE */
11, /* AVGWIDTH */
-1
};
static Lisp_Object
mac_do_list_fonts (pattern, maxnames)
const char *pattern;
int maxnames;
{
int i, n_fonts = 0;
Lisp_Object font_list = Qnil;
struct xlfdpat *pat;
char *scaled;
const char *ptr;
int scl_val[XLFD_SCL_LAST], *val;
const int *field;
int exact;
if (font_name_table == NULL) /* Initialize when first used. */
init_font_name_table ();
for (i = 0; i < XLFD_SCL_LAST; i++)
scl_val[i] = -1;
/* If the pattern contains 14 dashes and one of PIXEL_SIZE,
POINT_SIZE, and AVGWIDTH fields is explicitly specified, scalable
fonts are scaled according to the specified size. */
ptr = pattern;
i = 0;
field = xlfd_scalable_fields;
val = scl_val;
if (*ptr == '-')
do
{
ptr++;
if (i == *field)
{
if ('0' <= *ptr && *ptr <= '9')
{
*val = *ptr++ - '0';
while ('0' <= *ptr && *ptr <= '9' && *val < 10000)
*val = *val * 10 + *ptr++ - '0';
if (*ptr != '-')
*val = -1;
}
field++;
val++;
}
ptr = strchr (ptr, '-');
i++;
}
while (ptr && i < 14);
if (i == 14 && ptr == NULL)
{
if (scl_val[XLFD_SCL_PIXEL_SIZE] < 0)
scl_val[XLFD_SCL_PIXEL_SIZE] =
(scl_val[XLFD_SCL_POINT_SIZE] > 0 ? scl_val[XLFD_SCL_POINT_SIZE] / 10
: (scl_val[XLFD_SCL_AVGWIDTH] > 0 ? scl_val[XLFD_SCL_AVGWIDTH] / 10
: -1));
if (scl_val[XLFD_SCL_POINT_SIZE] < 0)
scl_val[XLFD_SCL_POINT_SIZE] =
(scl_val[XLFD_SCL_PIXEL_SIZE] > 0 ? scl_val[XLFD_SCL_PIXEL_SIZE] * 10
: (scl_val[XLFD_SCL_AVGWIDTH] > 0 ? scl_val[XLFD_SCL_AVGWIDTH]
: -1));
if (scl_val[XLFD_SCL_AVGWIDTH] < 0)
scl_val[XLFD_SCL_AVGWIDTH] =
(scl_val[XLFD_SCL_PIXEL_SIZE] > 0 ? scl_val[XLFD_SCL_PIXEL_SIZE] * 10
: (scl_val[XLFD_SCL_POINT_SIZE] > 0 ? scl_val[XLFD_SCL_POINT_SIZE]
: -1));
}
else
scl_val[XLFD_SCL_PIXEL_SIZE] = -1;
pat = xlfdpat_create (pattern);
if (pat == NULL)
return Qnil;
exact = xlfdpat_exact_p (pat);
for (i = 0; i < font_name_count; i++)
{
if (xlfdpat_match (pat, font_name_table[i]))
{
font_list = Fcons (build_string (font_name_table[i]), font_list);
if (exact || (maxnames > 0 && ++n_fonts >= maxnames))
break;
}
else if (scl_val[XLFD_SCL_PIXEL_SIZE] > 0
&& (ptr = strstr (font_name_table[i], "-0-0-0-0-m-0-")))
{
int former_len = ptr - font_name_table[i];
scaled = xmalloc (strlen (font_name_table[i]) + 20 + 1);
memcpy (scaled, font_name_table[i], former_len);
sprintf (scaled + former_len,
"-%d-%d-72-72-m-%d-%s",
scl_val[XLFD_SCL_PIXEL_SIZE],
scl_val[XLFD_SCL_POINT_SIZE],
scl_val[XLFD_SCL_AVGWIDTH],
ptr + sizeof ("-0-0-0-0-m-0-") - 1);
if (xlfdpat_match (pat, scaled))
{
font_list = Fcons (build_string (scaled), font_list);
xfree (scaled);
if (exact || (maxnames > 0 && ++n_fonts >= maxnames))
break;
}
else
xfree (scaled);
}
}
xlfdpat_destroy (pat);
return font_list;
}
/* Return a list of names of available fonts matching PATTERN on frame F.
Frame F null means we have not yet created any frame on Mac, and
consult the first display in x_display_list. MAXNAMES sets a limit
on how many fonts to match. */
Lisp_Object
x_list_fonts (f, pattern, size, maxnames)
struct frame *f;
Lisp_Object pattern;
int size, maxnames;
{
Lisp_Object list = Qnil, patterns, tem, key;
struct mac_display_info *dpyinfo
= f ? FRAME_MAC_DISPLAY_INFO (f) : x_display_list;
xassert (size <= 0);
patterns = Fassoc (pattern, Valternate_fontname_alist);
if (NILP (patterns))
patterns = Fcons (pattern, Qnil);
for (; CONSP (patterns); patterns = XCDR (patterns))
{
pattern = XCAR (patterns);
if (!STRINGP (pattern))
continue;
tem = XCAR (XCDR (dpyinfo->name_list_element));
key = Fcons (pattern, make_number (maxnames));
list = Fassoc (key, tem);
if (!NILP (list))
{
list = Fcdr_safe (list);
/* We have a cashed list. Don't have to get the list again. */
goto label_cached;
}
BLOCK_INPUT;
list = mac_do_list_fonts (SDATA (pattern), maxnames);
UNBLOCK_INPUT;
/* MAC_TODO: add code for matching outline fonts here */
/* Now store the result in the cache. */
XSETCAR (XCDR (dpyinfo->name_list_element),
Fcons (Fcons (key, list),
XCAR (XCDR (dpyinfo->name_list_element))));
label_cached:
if (NILP (list)) continue; /* Try the remaining alternatives. */
}
return list;
}
#if GLYPH_DEBUG
/* Check that FONT is valid on frame F. It is if it can be found in F's
font table. */
static void
x_check_font (f, font)
struct frame *f;
XFontStruct *font;
{
int i;
struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
xassert (font != NULL);
for (i = 0; i < dpyinfo->n_fonts; i++)
if (dpyinfo->font_table[i].name
&& font == dpyinfo->font_table[i].font)
break;
xassert (i < dpyinfo->n_fonts);
}
#endif /* GLYPH_DEBUG != 0 */
/* Set *W to the minimum width, *H to the minimum font height of FONT.
Note: There are (broken) X fonts out there with invalid XFontStruct
min_bounds contents. For example, handa@etl.go.jp reports that
"-adobe-courier-medium-r-normal--*-180-*-*-m-*-iso8859-1" fonts
have font->min_bounds.width == 0. */
static INLINE void
x_font_min_bounds (font, w, h)
MacFontStruct *font;
int *w, *h;
{
*h = FONT_HEIGHT (font);
*w = font->min_bounds.width;
}
/* Compute the smallest character width and smallest font height over
all fonts available on frame F. Set the members smallest_char_width
and smallest_font_height in F's x_display_info structure to
the values computed. Value is non-zero if smallest_font_height or
smallest_char_width become smaller than they were before. */
static int
x_compute_min_glyph_bounds (f)
struct frame *f;
{
int i;
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
MacFontStruct *font;
int old_width = dpyinfo->smallest_char_width;
int old_height = dpyinfo->smallest_font_height;
dpyinfo->smallest_font_height = 100000;
dpyinfo->smallest_char_width = 100000;
for (i = 0; i < dpyinfo->n_fonts; ++i)
if (dpyinfo->font_table[i].name)
{
struct font_info *fontp = dpyinfo->font_table + i;
int w, h;
font = (MacFontStruct *) fontp->font;
xassert (font != (MacFontStruct *) ~0);
x_font_min_bounds (font, &w, &h);
dpyinfo->smallest_font_height = min (dpyinfo->smallest_font_height, h);
dpyinfo->smallest_char_width = min (dpyinfo->smallest_char_width, w);
}
xassert (dpyinfo->smallest_char_width > 0
&& dpyinfo->smallest_font_height > 0);
return (dpyinfo->n_fonts == 1
|| dpyinfo->smallest_char_width < old_width
|| dpyinfo->smallest_font_height < old_height);
}
/* Determine whether given string is a fully-specified XLFD: all 14
fields are present, none is '*'. */
static int
is_fully_specified_xlfd (p)
const char *p;
{
int i;
char *q;
if (*p != '-')
return 0;
for (i = 0; i < 13; i++)
{
q = strchr (p + 1, '-');
if (q == NULL)
return 0;
if (q - p == 2 && *(p + 1) == '*')
return 0;
p = q;
}
if (strchr (p + 1, '-') != NULL)
return 0;
if (*(p + 1) == '*' && *(p + 2) == '\0')
return 0;
return 1;
}
/* mac_load_query_font creates and returns an internal representation
for a font in a MacFontStruct struct. There is really no concept
corresponding to "loading" a font on the Mac. But we check its
existence and find the font number and all other information for it
and store them in the returned MacFontStruct. */
static MacFontStruct *
mac_load_query_font (f, fontname)
struct frame *f;
char *fontname;
{
int size;
char *name;
Str255 family;
Str31 charset;
SInt16 fontnum;
#if USE_ATSUI
static ATSUFontID font_id;
ATSUStyle mac_style = NULL;
#endif
Style fontface;
#if TARGET_API_MAC_CARBON
TextEncoding encoding;
int scriptcode;
#else
short scriptcode;
#endif
MacFontStruct *font;
XCharStruct *space_bounds = NULL, *pcm;
if (is_fully_specified_xlfd (fontname))
name = fontname;
else
{
Lisp_Object matched_fonts;
matched_fonts = mac_do_list_fonts (fontname, 1);
if (NILP (matched_fonts))
return NULL;
name = SDATA (XCAR (matched_fonts));
}
if (parse_x_font_name (name, family, &size, &fontface, charset) == 0)
return NULL;
#if USE_ATSUI
if (strcmp (charset, "iso10646-1") == 0) /* XXX */
{
OSStatus err;
static const ATSUAttributeTag tags[] =
{kATSUFontTag, kATSUSizeTag,
kATSUQDBoldfaceTag, kATSUQDItalicTag};
static const ByteCount sizes[] =
{sizeof (ATSUFontID), sizeof (Fixed),
sizeof (Boolean), sizeof (Boolean)};
static Fixed size_fixed;
static Boolean bold_p, italic_p;
static const ATSUAttributeValuePtr values[] =
{&font_id, &size_fixed,
&bold_p, &italic_p};
static const ATSUFontFeatureType types[] =
{kAllTypographicFeaturesType, kDiacriticsType};
static const ATSUFontFeatureSelector selectors[] =
{kAllTypeFeaturesOffSelector, kDecomposeDiacriticsSelector};
FMFontStyle style;
font_id = atsu_find_font_from_family_name (family);
if (font_id == kATSUInvalidFontID)
return NULL;
size_fixed = Long2Fix (size);
bold_p = (fontface & bold) != 0;
italic_p = (fontface & italic) != 0;
err = ATSUCreateStyle (&mac_style);
if (err != noErr)
return NULL;
err = ATSUSetFontFeatures (mac_style, sizeof (types) / sizeof (types[0]),
types, selectors);
if (err != noErr)
return NULL;
err = ATSUSetAttributes (mac_style, sizeof (tags) / sizeof (tags[0]),
tags, sizes, values);
if (err != noErr)
return NULL;
err = FMGetFontFamilyInstanceFromFont (font_id, &fontnum, &style);
if (err != noErr)
fontnum = -1;
scriptcode = kTextEncodingMacUnicode;
}
else
#endif
{
Lisp_Object tmp = Fassoc (build_string (family), fm_font_family_alist);
if (NILP (tmp))
return NULL;
fontnum = XINT (XCDR (tmp));
#if TARGET_API_MAC_CARBON
if (FMGetFontFamilyTextEncoding (fontnum, &encoding) != noErr)
return NULL;
scriptcode = GetTextEncodingBase (encoding);
#else
scriptcode = FontToScript (fontnum);
#endif
}
font = (MacFontStruct *) xmalloc (sizeof (struct MacFontStruct));
font->mac_fontnum = fontnum;
font->mac_fontsize = size;
font->mac_fontface = fontface;
font->mac_scriptcode = scriptcode;
#if USE_ATSUI
font->mac_style = mac_style;
#if USE_CG_TEXT_DRAWING
font->cg_font = NULL;
font->cg_glyphs = NULL;
#endif
#endif
/* Apple Japanese (SJIS) font is listed as both
"*-jisx0208.1983-sjis" (Japanese script) and "*-jisx0201.1976-0"
(Roman script) in init_font_name_table (). The latter should be
treated as a one-byte font. */
if (scriptcode == smJapanese && strcmp (charset, "jisx0201.1976-0") == 0)
font->mac_scriptcode = smRoman;
font->full_name = mac_to_x_fontname (family, size, fontface, charset);
#if USE_ATSUI
if (font->mac_style)
{
OSStatus err;
UniChar c;
font->min_byte1 = 0;
font->max_byte1 = 0xff;
font->min_char_or_byte2 = 0;
font->max_char_or_byte2 = 0xff;
font->bounds.rows = xmalloc (sizeof (XCharStruct *) * 0x100);
bzero (font->bounds.rows, sizeof (XCharStruct *) * 0x100);
font->bounds.rows[0] = xmalloc (sizeof (XCharStruct) * 0x100);
pcm_init (font->bounds.rows[0], 0x100);
#if USE_CG_TEXT_DRAWING
if (fontnum != -1)
{
FMFontStyle style;
ATSFontRef ats_font;
err = FMGetFontFromFontFamilyInstance (fontnum, fontface,
&font_id, &style);
/* Use CG text drawing if italic/bold is not synthesized. */
if (err == noErr && style == fontface)
{
ats_font = FMGetATSFontRefFromFont (font_id);
font->cg_font = CGFontCreateWithPlatformFont (&ats_font);
}
}
if (font->cg_font)
{
font->cg_glyphs = xmalloc (sizeof (CGGlyph) * 0x100);
bzero (font->cg_glyphs, sizeof (CGGlyph) * 0x100);
}
#endif
space_bounds = font->bounds.rows[0] + 0x20;
err = mac_query_char_extents (font->mac_style, 0x20,
&font->ascent, &font->descent,
space_bounds,
#if USE_CG_TEXT_DRAWING
(font->cg_glyphs ? font->cg_glyphs + 0x20
: NULL)
#else
NULL
#endif
);
if (err != noErr
|| space_bounds->width <= 0 || FONT_HEIGHT (font) <= 0)
{
mac_unload_font (&one_mac_display_info, font);
return NULL;
}
pcm = font->bounds.rows[0];
for (c = 0x21; c <= 0xff; c++)
{
if (c == 0xad)
/* Soft hyphen is not supported in ATSUI. */
continue;
else if (c == 0x7f)
{
#if USE_CG_TEXT_DRAWING
if (font->cg_glyphs)
{
c = 0x9f;
pcm = NULL;
continue;
}
#endif
break;
}
mac_query_char_extents (font->mac_style, c, NULL, NULL,
pcm ? pcm + c : NULL,
#if USE_CG_TEXT_DRAWING
(font->cg_glyphs ? font->cg_glyphs + c
: NULL)
#else
NULL
#endif
);
#if USE_CG_TEXT_DRAWING
if (font->cg_glyphs && font->cg_glyphs[c] == 0)
{
/* Don't use CG text drawing if font substitution occurs in
ASCII or Latin-1 characters. */
CGFontRelease (font->cg_font);
font->cg_font = NULL;
xfree (font->cg_glyphs);
font->cg_glyphs = NULL;
if (pcm == NULL)
break;
}
#endif
}
}
else
#endif
{
OSStatus err;
FontInfo the_fontinfo;
int is_two_byte_font;
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
SetPortWindowPort (FRAME_MAC_WINDOW (f));
TextFont (fontnum);
TextSize (size);
TextFace (fontface);
GetFontInfo (&the_fontinfo);
font->ascent = the_fontinfo.ascent;
font->descent = the_fontinfo.descent;
is_two_byte_font = (font->mac_scriptcode == smJapanese
|| font->mac_scriptcode == smTradChinese
|| font->mac_scriptcode == smSimpChinese
|| font->mac_scriptcode == smKorean);
if (is_two_byte_font)
{
int char_width;
font->min_byte1 = 0xa1;
font->max_byte1 = 0xfe;
font->min_char_or_byte2 = 0xa1;
font->max_char_or_byte2 = 0xfe;
/* Use the width of an "ideographic space" of that font
because the_fontinfo.widMax returns the wrong width for
some fonts. */
switch (font->mac_scriptcode)
{
case smJapanese:
font->min_byte1 = 0x81;
font->max_byte1 = 0xfc;
font->min_char_or_byte2 = 0x40;
font->max_char_or_byte2 = 0xfc;
char_width = StringWidth("\p\x81\x40");
break;
case smTradChinese:
font->min_char_or_byte2 = 0x40;
char_width = StringWidth("\p\xa1\x40");
break;
case smSimpChinese:
char_width = StringWidth("\p\xa1\xa1");
break;
case smKorean:
char_width = StringWidth("\p\xa1\xa1");
break;
}
font->bounds.per_char = NULL;
if (fontface & italic)
font->max_bounds.rbearing = char_width + 1;
else
font->max_bounds.rbearing = char_width;
font->max_bounds.lbearing = 0;
font->max_bounds.width = char_width;
font->max_bounds.ascent = the_fontinfo.ascent;
font->max_bounds.descent = the_fontinfo.descent;
font->min_bounds = font->max_bounds;
}
else
{
int c;
font->min_byte1 = font->max_byte1 = 0;
font->min_char_or_byte2 = 0x20;
font->max_char_or_byte2 = 0xff;
font->bounds.per_char =
xmalloc (sizeof (XCharStruct) * (0xff - 0x20 + 1));
bzero (font->bounds.per_char,
sizeof (XCharStruct) * (0xff - 0x20 + 1));
space_bounds = font->bounds.per_char;
err = mac_query_char_extents (NULL, 0x20, &font->ascent,
&font->descent, space_bounds, NULL);
if (err != noErr || space_bounds->width <= 0)
{
mac_unload_font (&one_mac_display_info, font);
return NULL;
}
for (c = 0x21, pcm = space_bounds + 1; c <= 0xff; c++, pcm++)
mac_query_char_extents (NULL, c, NULL, NULL, pcm, NULL);
}
}
if (space_bounds)
{
int c;
font->min_bounds = font->max_bounds = *space_bounds;
for (c = 0x21, pcm = space_bounds + 1; c <= 0x7f; c++, pcm++)
if (pcm->width > 0)
{
font->min_bounds.lbearing = min (font->min_bounds.lbearing,
pcm->lbearing);
font->min_bounds.rbearing = min (font->min_bounds.rbearing,
pcm->rbearing);
font->min_bounds.width = min (font->min_bounds.width,
pcm->width);
font->min_bounds.ascent = min (font->min_bounds.ascent,
pcm->ascent);
font->min_bounds.descent = min (font->min_bounds.descent,
pcm->descent);
font->max_bounds.lbearing = max (font->max_bounds.lbearing,
pcm->lbearing);
font->max_bounds.rbearing = max (font->max_bounds.rbearing,
pcm->rbearing);
font->max_bounds.width = max (font->max_bounds.width,
pcm->width);
font->max_bounds.ascent = max (font->max_bounds.ascent,
pcm->ascent);
font->max_bounds.descent = max (font->max_bounds.descent,
pcm->descent);
}
if (
#if USE_ATSUI
font->mac_style == NULL &&
#endif
font->max_bounds.width == font->min_bounds.width
&& font->min_bounds.lbearing >= 0
&& font->max_bounds.rbearing <= font->max_bounds.width)
{
/* Fixed width and no overhangs. */
xfree (font->bounds.per_char);
font->bounds.per_char = NULL;
}
}
#if !defined (MAC_OS8) || USE_ATSUI
/* AppKit and WebKit do some adjustment to the heights of Courier,
Helvetica, and Times. This only works on the environments where
srcCopy text transfer mode is never used. */
if (
#ifdef MAC_OS8 /* implies USE_ATSUI */
font->mac_style &&
#endif
(strcmp (family, "courier") == 0 || strcmp (family, "helvetica") == 0
|| strcmp (family, "times") == 0))
font->ascent += (font->ascent + font->descent) * .15 + 0.5;
#endif
return font;
}
void
mac_unload_font (dpyinfo, font)
struct mac_display_info *dpyinfo;
XFontStruct *font;
{
xfree (font->full_name);
#if USE_ATSUI
if (font->mac_style)
{
int i;
for (i = font->min_byte1; i <= font->max_byte1; i++)
if (font->bounds.rows[i])
xfree (font->bounds.rows[i]);
xfree (font->bounds.rows);
ATSUDisposeStyle (font->mac_style);
}
else
#endif
if (font->bounds.per_char)
xfree (font->bounds.per_char);
#if USE_CG_TEXT_DRAWING
if (font->cg_font)
CGFontRelease (font->cg_font);
if (font->cg_glyphs)
xfree (font->cg_glyphs);
#endif
xfree (font);
}
/* Load font named FONTNAME of the size SIZE for frame F, and return a
pointer to the structure font_info while allocating it dynamically.
If SIZE is 0, load any size of font.
If loading is failed, return NULL. */
struct font_info *
x_load_font (f, fontname, size)
struct frame *f;
register char *fontname;
int size;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
Lisp_Object font_names;
/* Get a list of all the fonts that match this name. Once we
have a list of matching fonts, we compare them against the fonts
we already have by comparing names. */
font_names = x_list_fonts (f, build_string (fontname), size, 1);
if (!NILP (font_names))
{
Lisp_Object tail;
int i;
for (i = 0; i < dpyinfo->n_fonts; i++)
for (tail = font_names; CONSP (tail); tail = XCDR (tail))
if (dpyinfo->font_table[i].name
&& (!strcmp (dpyinfo->font_table[i].name,
SDATA (XCAR (tail)))
|| !strcmp (dpyinfo->font_table[i].full_name,
SDATA (XCAR (tail)))))
return (dpyinfo->font_table + i);
}
else
return NULL;
/* Load the font and add it to the table. */
{
struct MacFontStruct *font;
struct font_info *fontp;
int i;
fontname = (char *) SDATA (XCAR (font_names));
BLOCK_INPUT;
font = mac_load_query_font (f, fontname);
UNBLOCK_INPUT;
if (!font)
return NULL;
/* Find a free slot in the font table. */
for (i = 0; i < dpyinfo->n_fonts; ++i)
if (dpyinfo->font_table[i].name == NULL)
break;
/* If no free slot found, maybe enlarge the font table. */
if (i == dpyinfo->n_fonts
&& dpyinfo->n_fonts == dpyinfo->font_table_size)
{
int sz;
dpyinfo->font_table_size = max (16, 2 * dpyinfo->font_table_size);
sz = dpyinfo->font_table_size * sizeof *dpyinfo->font_table;
dpyinfo->font_table
= (struct font_info *) xrealloc (dpyinfo->font_table, sz);
}
fontp = dpyinfo->font_table + i;
if (i == dpyinfo->n_fonts)
++dpyinfo->n_fonts;
/* Now fill in the slots of *FONTP. */
BLOCK_INPUT;
bzero (fontp, sizeof (*fontp));
fontp->font = font;
fontp->font_idx = i;
fontp->charset = -1; /* fs_load_font sets it. */
fontp->name = (char *) xmalloc (strlen (fontname) + 1);
bcopy (fontname, fontp->name, strlen (fontname) + 1);
if (font->min_bounds.width == font->max_bounds.width)
{
/* Fixed width font. */
fontp->average_width = fontp->space_width = font->min_bounds.width;
}
else
{
XChar2b char2b;
XCharStruct *pcm;
char2b.byte1 = 0x00, char2b.byte2 = 0x20;
pcm = mac_per_char_metric (font, &char2b, 0);
if (pcm)
fontp->space_width = pcm->width;
else
fontp->space_width = FONT_WIDTH (font);
if (pcm)
{
int width = pcm->width;
for (char2b.byte2 = 33; char2b.byte2 <= 126; char2b.byte2++)
if ((pcm = mac_per_char_metric (font, &char2b, 0)) != NULL)
width += pcm->width;
fontp->average_width = width / 95;
}
else
fontp->average_width = FONT_WIDTH (font);
}
fontp->full_name = (char *) xmalloc (strlen (font->full_name) + 1);
bcopy (font->full_name, fontp->full_name, strlen (font->full_name) + 1);
fontp->size = font->max_bounds.width;
fontp->height = FONT_HEIGHT (font);
{
/* For some font, ascent and descent in max_bounds field is
larger than the above value. */
int max_height = font->max_bounds.ascent + font->max_bounds.descent;
if (max_height > fontp->height)
fontp->height = max_height;
}
/* MAC_TODO: The script encoding is irrelevant in unicode? */
/* The slot `encoding' specifies how to map a character
code-points (0x20..0x7F or 0x2020..0x7F7F) of each charset to
the font code-points (0:0x20..0x7F, 1:0xA0..0xFF), or
(0:0x2020..0x7F7F, 1:0xA0A0..0xFFFF, 3:0x20A0..0x7FFF,
2:0xA020..0xFF7F). For the moment, we don't know which charset
uses this font. So, we set information in fontp->encoding_type
which is never used by any charset. If mapping can't be
decided, set FONT_ENCODING_NOT_DECIDED. */
if (font->mac_scriptcode == smJapanese)
fontp->encoding_type = 4;
else
{
fontp->encoding_type
= (font->max_byte1 == 0
/* 1-byte font */
? (font->min_char_or_byte2 < 0x80
? (font->max_char_or_byte2 < 0x80
? 0 /* 0x20..0x7F */
: FONT_ENCODING_NOT_DECIDED) /* 0x20..0xFF */
: 1) /* 0xA0..0xFF */
/* 2-byte font */
: (font->min_byte1 < 0x80
? (font->max_byte1 < 0x80
? (font->min_char_or_byte2 < 0x80
? (font->max_char_or_byte2 < 0x80
? 0 /* 0x2020..0x7F7F */
: FONT_ENCODING_NOT_DECIDED) /* 0x2020..0x7FFF */
: 3) /* 0x20A0..0x7FFF */
: FONT_ENCODING_NOT_DECIDED) /* 0x20??..0xA0?? */
: (font->min_char_or_byte2 < 0x80
? (font->max_char_or_byte2 < 0x80
? 2 /* 0xA020..0xFF7F */
: FONT_ENCODING_NOT_DECIDED) /* 0xA020..0xFFFF */
: 1))); /* 0xA0A0..0xFFFF */
}
#if 0 /* MAC_TODO: fill these out with more reasonably values */
fontp->baseline_offset
= (XGetFontProperty (font, dpyinfo->Xatom_MULE_BASELINE_OFFSET, &value)
? (long) value : 0);
fontp->relative_compose
= (XGetFontProperty (font, dpyinfo->Xatom_MULE_RELATIVE_COMPOSE, &value)
? (long) value : 0);
fontp->default_ascent
= (XGetFontProperty (font, dpyinfo->Xatom_MULE_DEFAULT_ASCENT, &value)
? (long) value : 0);
#else
fontp->baseline_offset = 0;
fontp->relative_compose = 0;
fontp->default_ascent = 0;
#endif
/* Set global flag fonts_changed_p to non-zero if the font loaded
has a character with a smaller width than any other character
before, or if the font loaded has a smaller height than any
other font loaded before. If this happens, it will make a
glyph matrix reallocation necessary. */
fonts_changed_p |= x_compute_min_glyph_bounds (f);
UNBLOCK_INPUT;
return fontp;
}
}
/* Return a pointer to struct font_info of a font named FONTNAME for
frame F. If no such font is loaded, return NULL. */
struct font_info *
x_query_font (f, fontname)
struct frame *f;
register char *fontname;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
int i;
for (i = 0; i < dpyinfo->n_fonts; i++)
if (dpyinfo->font_table[i].name
&& (!xstricmp (dpyinfo->font_table[i].name, fontname)
|| !xstricmp (dpyinfo->font_table[i].full_name, fontname)))
return (dpyinfo->font_table + i);
return NULL;
}
/* Find a CCL program for a font specified by FONTP, and set the member
`encoder' of the structure. */
void
x_find_ccl_program (fontp)
struct font_info *fontp;
{
Lisp_Object list, elt;
for (list = Vfont_ccl_encoder_alist; CONSP (list); list = XCDR (list))
{
elt = XCAR (list);
if (CONSP (elt)
&& STRINGP (XCAR (elt))
&& (fast_c_string_match_ignore_case (XCAR (elt), fontp->name)
>= 0))
break;
}
if (! NILP (list))
{
struct ccl_program *ccl
= (struct ccl_program *) xmalloc (sizeof (struct ccl_program));
if (setup_ccl_program (ccl, XCDR (elt)) < 0)
xfree (ccl);
else
fontp->font_encoder = ccl;
}
}
#if USE_MAC_FONT_PANEL
/* Whether Font Panel has been shown before. The first call to font
panel functions (FPIsFontPanelVisible, SetFontInfoForSelection) is
slow. This variable is used for deferring such a call as much as
possible. */
static int font_panel_shown_p = 0;
extern Lisp_Object Qfont;
static Lisp_Object Qpanel_closed, Qselection;
static OSStatus mac_store_event_ref_as_apple_event P_ ((AEEventClass, AEEventID,
Lisp_Object,
Lisp_Object,
EventRef, UInt32,
const EventParamName *,
const EventParamType *));
int
mac_font_panel_visible_p ()
{
return font_panel_shown_p && FPIsFontPanelVisible ();
}
static pascal OSStatus
mac_handle_font_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus result, err;
Lisp_Object id_key;
int num_params;
const EventParamName *names;
const EventParamType *types;
static const EventParamName names_sel[] = {kEventParamATSUFontID,
kEventParamATSUFontSize,
kEventParamFMFontFamily,
kEventParamFMFontStyle,
kEventParamFMFontSize,
kEventParamFontColor};
static const EventParamType types_sel[] = {typeATSUFontID,
typeATSUSize,
typeFMFontFamily,
typeFMFontStyle,
typeFMFontSize,
typeFontColor};
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr)
return result;
switch (GetEventKind (event))
{
case kEventFontPanelClosed:
id_key = Qpanel_closed;
num_params = 0;
names = NULL;
types = NULL;
break;
case kEventFontSelection:
id_key = Qselection;
num_params = sizeof (names_sel) / sizeof (names_sel[0]);
names = names_sel;
types = types_sel;
break;
}
err = mac_store_event_ref_as_apple_event (0, 0, Qfont, id_key,
event, num_params,
names, types);
if (err == noErr)
result = noErr;
return result;
}
OSStatus
mac_show_hide_font_panel ()
{
if (!font_panel_shown_p)
{
OSStatus err;
static const EventTypeSpec specs[] =
{{kEventClassFont, kEventFontPanelClosed},
{kEventClassFont, kEventFontSelection}};
err = InstallApplicationEventHandler (mac_handle_font_event,
GetEventTypeCount (specs),
specs, NULL, NULL);
if (err != noErr)
return err;
font_panel_shown_p = 1;
}
return FPShowHideFontPanel ();
}
OSStatus
mac_set_font_info_for_selection (f, face_id, c)
struct frame *f;
int face_id, c;
{
OSStatus err;
EventTargetRef target = NULL;
XFontStruct *font = NULL;
if (!mac_font_panel_visible_p ())
return noErr;
if (f)
{
target = GetWindowEventTarget (FRAME_MAC_WINDOW (f));
if (FRAME_FACE_CACHE (f) && CHAR_VALID_P (c, 0))
{
struct face *face;
face_id = FACE_FOR_CHAR (f, FACE_FROM_ID (f, face_id), c);
face = FACE_FROM_ID (f, face_id);
font = face->font;
}
}
if (font == NULL)
err = SetFontInfoForSelection (kFontSelectionATSUIType, 0, NULL, target);
else
{
if (font->mac_fontnum != -1)
{
FontSelectionQDStyle qd_style;
qd_style.version = kFontSelectionQDStyleVersionZero;
qd_style.instance.fontFamily = font->mac_fontnum;
qd_style.instance.fontStyle = font->mac_fontface;
qd_style.size = font->mac_fontsize;
qd_style.hasColor = false;
err = SetFontInfoForSelection (kFontSelectionQDType,
1, &qd_style, target);
}
else
err = SetFontInfoForSelection (kFontSelectionATSUIType,
1, &font->mac_style, target);
}
return err;
}
#endif
/* The Mac Event loop code */
#if !TARGET_API_MAC_CARBON
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if __MWERKS__
#include
#endif
#endif /* ! TARGET_API_MAC_CARBON */
#define M_APPLE 234
#define I_ABOUT 1
#define DEFAULT_NUM_COLS 80
#define MIN_DOC_SIZE 64
#define MAX_DOC_SIZE 32767
#define EXTRA_STACK_ALLOC (256 * 1024)
#define ARGV_STRING_LIST_ID 129
#define ABOUT_ALERT_ID 128
#define RAM_TOO_LARGE_ALERT_ID 129
/* Contains the string "reverse", which is a constant for mouse button emu.*/
Lisp_Object Qreverse;
/* Modifier associated with the control key, or nil to ignore. */
Lisp_Object Vmac_control_modifier;
/* Modifier associated with the option key, or nil to ignore. */
Lisp_Object Vmac_option_modifier;
/* Modifier associated with the command key, or nil to ignore. */
Lisp_Object Vmac_command_modifier;
/* Modifier associated with the function key, or nil to ignore. */
Lisp_Object Vmac_function_modifier;
/* True if the option and command modifiers should be used to emulate
a three button mouse */
Lisp_Object Vmac_emulate_three_button_mouse;
#if TARGET_API_MAC_CARBON
/* Non-zero if the mouse wheel button (i.e. button 4) should map to
mouse-2, instead of mouse-3. */
int mac_wheel_button_is_mouse_2;
/* If non-zero, the Mac "Command" key is passed on to the Mac Toolbox
for processing before Emacs sees it. */
int mac_pass_command_to_system;
/* If non-zero, the Mac "Control" key is passed on to the Mac Toolbox
for processing before Emacs sees it. */
int mac_pass_control_to_system;
#endif
/* Points to the variable `inev' in the function XTread_socket. It is
used for passing an input event to the function back from
Carbon/Apple event handlers. */
static struct input_event *read_socket_inev = NULL;
/* Whether or not the screen configuration has changed. */
static int mac_screen_config_changed = 0;
Point saved_menu_event_location;
/* Apple Events */
#if TARGET_API_MAC_CARBON
static Lisp_Object Qhi_command;
#ifdef MAC_OSX
extern Lisp_Object Qwindow;
static Lisp_Object Qtoolbar_switch_mode;
#endif
#if USE_MAC_TSM
static TSMDocumentID tsm_document_id;
Lisp_Object Qtext_input;
Lisp_Object Qupdate_active_input_area, Qunicode_for_key_event;
Lisp_Object Vmac_ts_active_input_overlay, Vmac_ts_active_input_buf;
extern Lisp_Object Qbefore_string;
static Lisp_Object Vmac_ts_script_language_on_focus;
static Lisp_Object saved_ts_script_language_on_focus;
static ScriptLanguageRecord saved_ts_language;
static Component saved_ts_component;
#endif
#endif /* TARGET_API_MAC_CARBON */
extern int mac_ready_for_apple_events;
extern Lisp_Object Qundefined;
extern void init_apple_event_handler P_ ((void));
extern void mac_find_apple_event_spec P_ ((AEEventClass, AEEventID,
Lisp_Object *, Lisp_Object *,
Lisp_Object *));
extern OSErr init_coercion_handler P_ ((void));
/* Drag and Drop */
extern OSErr install_drag_handler P_ ((WindowRef));
extern void remove_drag_handler P_ ((WindowRef));
#if TARGET_API_MAC_CARBON
/* Showing help echo string during menu tracking */
extern OSStatus install_menu_target_item_handler P_ ((void));
#ifdef MAC_OSX
extern OSStatus install_service_handler ();
Lisp_Object Qservice, Qpaste, Qperform;
Lisp_Object Qmouse_drag_overlay;
#endif
#endif
extern void init_emacs_passwd_dir ();
extern int emacs_main (int, char **, char **);
extern void initialize_applescript();
extern void terminate_applescript();
/* Table for translating Mac keycode to X keysym values. Contributed
by Sudhir Shenoy.
Mapping for special keys is now identical to that in Apple X11
except `clear' (-> ) on the KeyPad, `enter' (-> )
on the right of the Cmd key on laptops, and fn + `enter' (->
). */
static const unsigned char keycode_to_xkeysym_table[] = {
/*0x00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*0x10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*0x20*/ 0, 0, 0, 0, 0x0d /*return*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*0x30*/ 0x09 /*tab*/, 0 /*0x0020 space*/, 0, 0x08 /*backspace*/,
/*0x34*/ 0x8d /*enter on laptops*/, 0x1b /*escape*/, 0, 0,
/*0x38*/ 0, 0, 0, 0,
/*0x3C*/ 0, 0, 0, 0,
/*0x40*/ 0, 0xae /*kp-decimal*/, 0, 0xaa /*kp-multiply*/,
/*0x44*/ 0, 0xab /*kp-add*/, 0, 0x0b /*clear*/,
/*0x48*/ 0, 0, 0, 0xaf /*kp-divide*/,
/*0x4C*/ 0x8d /*kp-enter*/, 0, 0xad /*kp-subtract*/, 0,
/*0x50*/ 0, 0xbd /*kp-equal*/, 0xb0 /*kp-0*/, 0xb1 /*kp-1*/,
/*0x54*/ 0xb2 /*kp-2*/, 0xb3 /*kp-3*/, 0xb4 /*kp-4*/, 0xb5 /*kp-5*/,
/*0x58*/ 0xb6 /*kp-6*/, 0xb7 /*kp-7*/, 0, 0xb8 /*kp-8*/,
/*0x5C*/ 0xb9 /*kp-9*/, 0, 0, 0,
/*0x60*/ 0xc2 /*f5*/, 0xc3 /*f6*/, 0xc4 /*f7*/, 0xc0 /*f3*/,
/*0x64*/ 0xc5 /*f8*/, 0xc6 /*f9*/, 0, 0xc8 /*f11*/,
/*0x68*/ 0, 0xca /*f13*/, 0xcd /*f16*/, 0xcb /*f14*/,
/*0x6C*/ 0, 0xc7 /*f10*/, 0x0a /*fn+enter on laptops*/, 0xc9 /*f12*/,
/*0x70*/ 0, 0xcc /*f15*/, 0x6a /*help*/, 0x50 /*home*/,
/*0x74*/ 0x55 /*pgup*/, 0xff /*delete*/, 0xc1 /*f4*/, 0x57 /*end*/,
/*0x78*/ 0xbf /*f2*/, 0x56 /*pgdown*/, 0xbe /*f1*/, 0x51 /*left*/,
/*0x7C*/ 0x53 /*right*/, 0x54 /*down*/, 0x52 /*up*/, 0
};
#ifdef MAC_OSX
/* Table for translating Mac keycode with the laptop `fn' key to that
without it. Destination symbols in comments are keys on US
keyboard, and they may not be the same on other types of keyboards.
If the destination is identical to the source (f1 ... f12), it
doesn't map `fn' key to a modifier. */
static const unsigned char fn_keycode_to_keycode_table[] = {
/*0x00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*0x10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*0x20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*0x30*/ 0, 0, 0, 0,
/*0x34*/ 0, 0, 0, 0,
/*0x38*/ 0, 0, 0, 0,
/*0x3C*/ 0, 0, 0, 0,
/*0x40*/ 0, 0x2f /*kp-decimal -> '.'*/, 0, 0x23 /*kp-multiply -> 'p'*/,
/*0x44*/ 0, 0x2c /*kp-add -> '/'*/, 0, 0x16 /*clear -> '6'*/,
/*0x48*/ 0, 0, 0, 0x1d /*kp-/ -> '0'*/,
/*0x4C*/ 0x24 /*kp-enter -> return*/, 0, 0x29 /*kp-subtract -> ';'*/, 0,
/*0x50*/ 0, 0x1b /*kp-equal -> '-'*/, 0x2e /*kp-0 -> 'm'*/, 0x26 /*kp-1 -> 'j'*/,
/*0x54*/ 0x28 /*kp-2 -> 'k'*/, 0x25 /*kp-3 -> 'l'*/, 0x20 /*kp-4 -> 'u'*/, 0x22 /*kp-5 ->'i'*/,
/*0x58*/ 0x1f /*kp-6 -> 'o'*/, 0x1a /*kp-7 -> '7'*/, 0, 0x1c /*kp-8 -> '8'*/,
/*0x5C*/ 0x19 /*kp-9 -> '9'*/, 0, 0, 0,
/*0x60*/ 0x60 /*f5 = f5*/, 0x61 /*f6 = f6*/, 0x62 /*f7 = f7*/, 0x63 /*f3 = f3*/,
/*0x64*/ 0x64 /*f8 = f8*/, 0x65 /*f9 = f9*/, 0, 0x67 /*f11 = f11*/,
/*0x68*/ 0, 0, 0, 0,
/*0x6C*/ 0, 0x6d /*f10 = f10*/, 0, 0x6f /*f12 = f12*/,
/*0x70*/ 0, 0, 0, 0x7b /*home -> left*/,
/*0x74*/ 0x7e /*pgup -> up*/, 0x33 /*delete -> backspace*/, 0x76 /*f4 = f4*/, 0x7c /*end -> right*/,
/*0x78*/ 0x78 /*f2 = f2*/, 0x7d /*pgdown -> down*/, 0x7a /*f1 = f1*/, 0,
/*0x7C*/ 0, 0, 0, 0
};
#endif /* MAC_OSX */
static int
#if TARGET_API_MAC_CARBON
mac_to_emacs_modifiers (UInt32 mods)
#else
mac_to_emacs_modifiers (EventModifiers mods)
#endif
{
unsigned int result = 0;
if (mods & shiftKey)
result |= shift_modifier;
/* Deactivated to simplify configuration:
if Vmac_option_modifier is non-NIL, we fully process the Option
key. Otherwise, we only process it if an additional Ctrl or Command
is pressed. That way the system may convert the character to a
composed one.
if ((mods & optionKey) &&
(( !NILP(Vmac_option_modifier) ||
((mods & cmdKey) || (mods & controlKey))))) */
if (!NILP (Vmac_option_modifier) && (mods & optionKey)) {
Lisp_Object val = Fget(Vmac_option_modifier, Qmodifier_value);
if (INTEGERP(val))
result |= XUINT(val);
}
if (!NILP (Vmac_command_modifier) && (mods & cmdKey)) {
Lisp_Object val = Fget(Vmac_command_modifier, Qmodifier_value);
if (INTEGERP(val))
result |= XUINT(val);
}
if (!NILP (Vmac_control_modifier) && (mods & controlKey)) {
Lisp_Object val = Fget(Vmac_control_modifier, Qmodifier_value);
if (INTEGERP(val))
result |= XUINT(val);
}
#ifdef MAC_OSX
if (!NILP (Vmac_function_modifier) && (mods & kEventKeyModifierFnMask)) {
Lisp_Object val = Fget(Vmac_function_modifier, Qmodifier_value);
if (INTEGERP(val))
result |= XUINT(val);
}
#endif
return result;
}
static UInt32
mac_mapped_modifiers (modifiers)
UInt32 modifiers;
{
UInt32 mapped_modifiers_all =
(NILP (Vmac_control_modifier) ? 0 : controlKey)
| (NILP (Vmac_option_modifier) ? 0 : optionKey)
| (NILP (Vmac_command_modifier) ? 0 : cmdKey);
#ifdef MAC_OSX
mapped_modifiers_all |=
(NILP (Vmac_function_modifier) ? 0 : kEventKeyModifierFnMask);
#endif
return mapped_modifiers_all & modifiers;
}
static int
mac_get_emulated_btn ( UInt32 modifiers )
{
int result = 0;
if (!NILP (Vmac_emulate_three_button_mouse)) {
int cmdIs3 = !EQ (Vmac_emulate_three_button_mouse, Qreverse);
if (modifiers & cmdKey)
result = cmdIs3 ? 2 : 1;
else if (modifiers & optionKey)
result = cmdIs3 ? 1 : 2;
}
return result;
}
#ifdef MAC_OSX
void
mac_get_selected_range (w, range)
struct window *w;
CFRange *range;
{
Lisp_Object overlay = find_symbol_value (Qmouse_drag_overlay);
struct buffer *b = XBUFFER (w->buffer);
int begv = BUF_BEGV (b), zv = BUF_ZV (b);
int start, end;
if (OVERLAYP (overlay)
&& EQ (Foverlay_buffer (overlay), w->buffer)
&& (start = XINT (Foverlay_start (overlay)),
end = XINT (Foverlay_end (overlay)),
start != end))
;
else
{
if (w == XWINDOW (selected_window) && b == current_buffer)
start = PT;
else
start = marker_position (w->pointm);
if (NILP (Vtransient_mark_mode) || NILP (b->mark_active))
end = start;
else
{
int mark_pos = marker_position (b->mark);
if (start <= mark_pos)
end = mark_pos;
else
{
end = start;
start = mark_pos;
}
}
}
if (start != end)
{
if (start < begv)
start = begv;
else if (start > zv)
start = zv;
if (end < begv)
end = begv;
else if (end > zv)
end = zv;
}
range->location = start - begv;
range->length = end - start;
}
/* Store the text of the buffer BUF from START to END as Unicode
characters in CHARACTERS. Return non-zero if successful. */
int
mac_store_buffer_text_to_unicode_chars (buf, start, end, characters)
struct buffer *buf;
int start, end;
UniChar *characters;
{
int start_byte, end_byte, char_count, byte_count;
struct coding_system coding;
unsigned char *dst = (unsigned char *) characters;
start_byte = buf_charpos_to_bytepos (buf, start);
end_byte = buf_charpos_to_bytepos (buf, end);
char_count = end - start;
byte_count = end_byte - start_byte;
if (setup_coding_system (
#ifdef WORDS_BIG_ENDIAN
intern ("utf-16be")
#else
intern ("utf-16le")
#endif
, &coding) < 0)
return 0;
coding.src_multibyte = !NILP (buf->enable_multibyte_characters);
coding.dst_multibyte = 0;
coding.mode |= CODING_MODE_LAST_BLOCK;
coding.composing = COMPOSITION_DISABLED;
if (BUF_GPT_BYTE (buf) <= start_byte || end_byte <= BUF_GPT_BYTE (buf))
encode_coding (&coding, BUF_BYTE_ADDRESS (buf, start_byte), dst,
byte_count, char_count * sizeof (UniChar));
else
{
int first_byte_count = BUF_GPT_BYTE (buf) - start_byte;
encode_coding (&coding, BUF_BYTE_ADDRESS (buf, start_byte), dst,
first_byte_count, char_count * sizeof (UniChar));
if (coding.result == CODING_FINISH_NORMAL)
encode_coding (&coding,
BUF_BYTE_ADDRESS (buf, start_byte + first_byte_count),
dst + coding.produced,
byte_count - first_byte_count,
char_count * sizeof (UniChar) - coding.produced);
}
if (coding.result != CODING_FINISH_NORMAL)
return 0;
return 1;
}
void
mac_ax_selected_text_range (f, range)
struct frame *f;
CFRange *range;
{
mac_get_selected_range (XWINDOW (f->selected_window), range);
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
unsigned int
mac_ax_number_of_characters (f)
struct frame *f;
{
struct buffer *b = XBUFFER (XWINDOW (f->selected_window)->buffer);
return BUF_ZV (b) - BUF_BEGV (b);
}
#endif
#endif
#if USE_MAC_TSM
OSStatus
mac_restore_keyboard_input_source ()
{
OSStatus err = noErr;
ScriptLanguageRecord slrec, *slptr = NULL;
if (EQ (Vmac_ts_script_language_on_focus, Qt)
&& EQ (saved_ts_script_language_on_focus, Qt))
slptr = &saved_ts_language;
else if (CONSP (Vmac_ts_script_language_on_focus)
&& INTEGERP (XCAR (Vmac_ts_script_language_on_focus))
&& INTEGERP (XCDR (Vmac_ts_script_language_on_focus))
&& CONSP (saved_ts_script_language_on_focus)
&& EQ (XCAR (saved_ts_script_language_on_focus),
XCAR (Vmac_ts_script_language_on_focus))
&& EQ (XCDR (saved_ts_script_language_on_focus),
XCDR (Vmac_ts_script_language_on_focus)))
{
slrec.fScript = XINT (XCAR (Vmac_ts_script_language_on_focus));
slrec.fLanguage = XINT (XCDR (Vmac_ts_script_language_on_focus));
slptr = &slrec;
}
if (slptr)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
err = SetDefaultInputMethodOfClass (saved_ts_component, slptr,
kKeyboardInputMethodClass);
#else
err = SetDefaultInputMethod (saved_ts_component, slptr);
#endif
if (err == noErr)
err = SetTextServiceLanguage (slptr);
/* Seems to be needed on Mac OS X 10.2. */
if (err == noErr)
KeyScript (slptr->fScript | smKeyForceKeyScriptMask);
}
return err;
}
void
mac_save_keyboard_input_source ()
{
OSStatus err;
ScriptLanguageRecord slrec, *slptr = NULL;
saved_ts_script_language_on_focus = Vmac_ts_script_language_on_focus;
if (EQ (Vmac_ts_script_language_on_focus, Qt))
{
err = GetTextServiceLanguage (&saved_ts_language);
if (err == noErr)
slptr = &saved_ts_language;
}
else if (CONSP (Vmac_ts_script_language_on_focus)
&& INTEGERP (XCAR (Vmac_ts_script_language_on_focus))
&& INTEGERP (XCDR (Vmac_ts_script_language_on_focus)))
{
slrec.fScript = XINT (XCAR (Vmac_ts_script_language_on_focus));
slrec.fLanguage = XINT (XCDR (Vmac_ts_script_language_on_focus));
slptr = &slrec;
}
if (slptr)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
GetDefaultInputMethodOfClass (&saved_ts_component, slptr,
kKeyboardInputMethodClass);
#else
GetDefaultInputMethod (&saved_ts_component, slptr);
#endif
}
}
#endif
#if TARGET_API_MAC_CARBON
/***** Code to handle C-g testing *****/
extern int quit_char;
extern int make_ctrl_char P_ ((int));
int
mac_quit_char_key_p (modifiers, key_code)
UInt32 modifiers, key_code;
{
UInt32 char_code;
unsigned long some_state = 0;
Ptr kchr_ptr = (Ptr) GetScriptManagerVariable (smKCHRCache);
int c, emacs_modifiers;
/* Mask off modifier keys that are mapped to some Emacs modifiers. */
key_code |= (modifiers & ~(mac_mapped_modifiers (modifiers)));
char_code = KeyTranslate (kchr_ptr, key_code, &some_state);
if (char_code & ~0xff)
return 0;
emacs_modifiers = mac_to_emacs_modifiers (modifiers);
if (emacs_modifiers & ctrl_modifier)
c = make_ctrl_char (char_code);
c |= (emacs_modifiers
& (meta_modifier | alt_modifier
| hyper_modifier | super_modifier));
return c == quit_char;
}
#endif
#if TARGET_API_MAC_CARBON
/* Obtains the event modifiers from the event ref and then calls
mac_to_emacs_modifiers. */
static int
mac_event_to_emacs_modifiers (EventRef eventRef)
{
UInt32 mods = 0, class;
GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32, NULL,
sizeof (UInt32), NULL, &mods);
class = GetEventClass (eventRef);
if (!NILP (Vmac_emulate_three_button_mouse) &&
(class == kEventClassMouse || class == kEventClassCommand))
{
mods &= ~(optionKey | cmdKey);
}
return mac_to_emacs_modifiers (mods);
}
/* Given an event ref, return the code to use for the mouse button
code in the emacs input_event. */
static int
mac_get_mouse_btn (EventRef ref)
{
EventMouseButton result = kEventMouseButtonPrimary;
GetEventParameter (ref, kEventParamMouseButton, typeMouseButton, NULL,
sizeof (EventMouseButton), NULL, &result);
switch (result)
{
case kEventMouseButtonPrimary:
if (NILP (Vmac_emulate_three_button_mouse))
return 0;
else {
UInt32 mods = 0;
GetEventParameter (ref, kEventParamKeyModifiers, typeUInt32, NULL,
sizeof (UInt32), NULL, &mods);
return mac_get_emulated_btn(mods);
}
case kEventMouseButtonSecondary:
return mac_wheel_button_is_mouse_2 ? 2 : 1;
case kEventMouseButtonTertiary:
case 4: /* 4 is the number for the mouse wheel button */
return mac_wheel_button_is_mouse_2 ? 1 : 2;
default:
return 0;
}
}
/* Normally, ConvertEventRefToEventRecord will correctly handle all
events. However the click of the mouse wheel is not converted to a
mouseDown or mouseUp event. Likewise for dead key events. This
calls ConvertEventRefToEventRecord, but then checks to see if it is
a mouse up/down, or a dead key Carbon event that has not been
converted, and if so, converts it by hand (to be picked up in the
XTread_socket loop). */
static Boolean mac_convert_event_ref (EventRef eventRef, EventRecord *eventRec)
{
OSStatus err;
Boolean result = ConvertEventRefToEventRecord (eventRef, eventRec);
EventKind action;
if (result)
return result;
switch (GetEventClass (eventRef))
{
case kEventClassMouse:
switch (GetEventKind (eventRef))
{
case kEventMouseDown:
eventRec->what = mouseDown;
result = 1;
break;
case kEventMouseUp:
eventRec->what = mouseUp;
result = 1;
break;
default:
break;
}
break;
case kEventClassKeyboard:
switch (GetEventKind (eventRef))
{
case kEventRawKeyDown:
action = keyDown;
goto keystroke_common;
case kEventRawKeyRepeat:
action = autoKey;
goto keystroke_common;
case kEventRawKeyUp:
action = keyUp;
keystroke_common:
{
unsigned char char_codes;
UInt32 key_code;
err = GetEventParameter (eventRef, kEventParamKeyMacCharCodes,
typeChar, NULL, sizeof (char),
NULL, &char_codes);
if (err == noErr)
err = GetEventParameter (eventRef, kEventParamKeyCode,
typeUInt32, NULL, sizeof (UInt32),
NULL, &key_code);
if (err == noErr)
{
eventRec->what = action;
eventRec->message = char_codes | ((key_code & 0xff) << 8);
result = 1;
}
}
break;
default:
break;
}
break;
default:
break;
}
if (result)
{
/* Need where and when. */
UInt32 mods = 0;
GetEventParameter (eventRef, kEventParamMouseLocation, typeQDPoint,
NULL, sizeof (Point), NULL, &eventRec->where);
/* Use two step process because new event modifiers are 32-bit
and old are 16-bit. Currently, only loss is NumLock & Fn. */
GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32,
NULL, sizeof (UInt32), NULL, &mods);
eventRec->modifiers = mods;
eventRec->when = EventTimeToTicks (GetEventTime (eventRef));
}
return result;
}
#endif
#ifdef MAC_OS8
static void
do_get_menus (void)
{
Handle menubar_handle;
MenuRef menu;
menubar_handle = GetNewMBar (128);
if(menubar_handle == NULL)
abort ();
SetMenuBar (menubar_handle);
DrawMenuBar ();
#if !TARGET_API_MAC_CARBON
menu = GetMenuRef (M_APPLE);
if (menu != NULL)
AppendResMenu (menu, 'DRVR');
else
abort ();
#endif
}
static void
do_init_managers (void)
{
#if !TARGET_API_MAC_CARBON
InitGraf (&qd.thePort);
InitFonts ();
FlushEvents (everyEvent, 0);
InitWindows ();
InitMenus ();
TEInit ();
InitDialogs (NULL);
#endif /* !TARGET_API_MAC_CARBON */
InitCursor ();
#if !TARGET_API_MAC_CARBON
/* set up some extra stack space for use by emacs */
SetApplLimit ((Ptr) ((long) GetApplLimit () - EXTRA_STACK_ALLOC));
/* MaxApplZone must be called for AppleScript to execute more
complicated scripts */
MaxApplZone ();
MoreMasters ();
#endif /* !TARGET_API_MAC_CARBON */
}
static void
do_check_ram_size (void)
{
SInt32 physical_ram_size, logical_ram_size;
if (Gestalt (gestaltPhysicalRAMSize, &physical_ram_size) != noErr
|| Gestalt (gestaltLogicalRAMSize, &logical_ram_size) != noErr
|| physical_ram_size > (1 << VALBITS)
|| logical_ram_size > (1 << VALBITS))
{
StopAlert (RAM_TOO_LARGE_ALERT_ID, NULL);
exit (1);
}
}
#endif /* MAC_OS8 */
static void
do_window_update (WindowRef win)
{
struct frame *f = mac_window_to_frame (win);
BeginUpdate (win);
/* The tooltip has been drawn already. Avoid the SET_FRAME_GARBAGED
below. */
if (win != tip_window)
{
if (f->async_visible == 0)
{
/* Update events may occur when a frame gets iconified. */
#if 0
f->async_visible = 1;
f->async_iconified = 0;
SET_FRAME_GARBAGED (f);
#endif
}
else
{
Rect r;
#if TARGET_API_MAC_CARBON
RgnHandle region = NewRgn ();
GetPortVisibleRegion (GetWindowPort (win), region);
GetRegionBounds (region, &r);
expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top);
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (f);
#endif
UpdateControls (win, region);
DisposeRgn (region);
#else
r = (*win->visRgn)->rgnBBox;
expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top);
UpdateControls (win, win->visRgn);
#endif
}
}
EndUpdate (win);
}
static int
is_emacs_window (WindowRef win)
{
Lisp_Object tail, frame;
if (!win)
return 0;
FOR_EACH_FRAME (tail, frame)
if (FRAME_MAC_P (XFRAME (frame)))
if (FRAME_MAC_WINDOW (XFRAME (frame)) == win)
return 1;
return 0;
}
#if USE_MAC_TSM
static OSStatus
mac_tsm_resume ()
{
OSStatus err;
ScriptLanguageRecord slrec, *slptr = NULL;
err = ActivateTSMDocument (tsm_document_id);
if (err == noErr)
{
if (EQ (Vmac_ts_script_language_on_focus, Qt)
&& EQ (saved_ts_script_language_on_focus, Qt))
slptr = &saved_ts_language;
else if (CONSP (Vmac_ts_script_language_on_focus)
&& INTEGERP (XCAR (Vmac_ts_script_language_on_focus))
&& INTEGERP (XCDR (Vmac_ts_script_language_on_focus))
&& CONSP (saved_ts_script_language_on_focus)
&& EQ (XCAR (saved_ts_script_language_on_focus),
XCAR (Vmac_ts_script_language_on_focus))
&& EQ (XCDR (saved_ts_script_language_on_focus),
XCDR (Vmac_ts_script_language_on_focus)))
{
slrec.fScript = XINT (XCAR (Vmac_ts_script_language_on_focus));
slrec.fLanguage = XINT (XCDR (Vmac_ts_script_language_on_focus));
slptr = &slrec;
}
}
if (slptr)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
err = SetDefaultInputMethodOfClass (saved_ts_component, slptr,
kKeyboardInputMethodClass);
#else
err = SetDefaultInputMethod (saved_ts_component, slptr);
#endif
if (err == noErr)
err = SetTextServiceLanguage (slptr);
/* Seems to be needed on Mac OS X 10.2. */
if (err == noErr)
KeyScript (slptr->fScript | smKeyForceKeyScriptMask);
}
return err;
}
static OSStatus
mac_tsm_suspend ()
{
OSStatus err;
ScriptLanguageRecord slrec, *slptr = NULL;
saved_ts_script_language_on_focus = Vmac_ts_script_language_on_focus;
if (EQ (Vmac_ts_script_language_on_focus, Qt))
{
err = GetTextServiceLanguage (&saved_ts_language);
if (err == noErr)
slptr = &saved_ts_language;
}
else if (CONSP (Vmac_ts_script_language_on_focus)
&& INTEGERP (XCAR (Vmac_ts_script_language_on_focus))
&& INTEGERP (XCDR (Vmac_ts_script_language_on_focus)))
{
slrec.fScript = XINT (XCAR (Vmac_ts_script_language_on_focus));
slrec.fLanguage = XINT (XCDR (Vmac_ts_script_language_on_focus));
slptr = &slrec;
}
if (slptr)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
GetDefaultInputMethodOfClass (&saved_ts_component, slptr,
kKeyboardInputMethodClass);
#else
GetDefaultInputMethod (&saved_ts_component, slptr);
#endif
}
err = DeactivateTSMDocument (tsm_document_id);
return err;
}
#endif
#if !TARGET_API_MAC_CARBON
void
do_apple_menu (SInt16 menu_item)
{
Str255 item_name;
SInt16 da_driver_refnum;
if (menu_item == I_ABOUT)
NoteAlert (ABOUT_ALERT_ID, NULL);
else
{
GetMenuItemText (GetMenuRef (M_APPLE), menu_item, item_name);
da_driver_refnum = OpenDeskAcc (item_name);
}
}
#endif /* !TARGET_API_MAC_CARBON */
/* Handle drags in size box. Based on code contributed by Ben
Mesander and IM - Window Manager A. */
static void
do_grow_window (w, e)
WindowRef w;
const EventRecord *e;
{
Rect limit_rect;
int rows, columns, width, height;
struct frame *f = mac_window_to_frame (w);
XSizeHints *size_hints = FRAME_SIZE_HINTS (f);
int min_width = MIN_DOC_SIZE, min_height = MIN_DOC_SIZE;
#if TARGET_API_MAC_CARBON
Rect new_rect;
#else
long grow_size;
#endif
if (size_hints->flags & PMinSize)
{
min_width = size_hints->min_width;
min_height = size_hints->min_height;
}
SetRect (&limit_rect, min_width, min_height, MAX_DOC_SIZE, MAX_DOC_SIZE);
#if TARGET_API_MAC_CARBON
if (!ResizeWindow (w, e->where, &limit_rect, &new_rect))
return;
height = new_rect.bottom - new_rect.top;
width = new_rect.right - new_rect.left;
#else
grow_size = GrowWindow (w, e->where, &limit_rect);
/* see if it really changed size */
if (grow_size == 0)
return;
height = HiWord (grow_size);
width = LoWord (grow_size);
#endif
if (width != FRAME_PIXEL_WIDTH (f)
|| height != FRAME_PIXEL_HEIGHT (f))
{
rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height);
columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width);
x_set_window_size (f, 0, columns, rows);
}
}
#if TARGET_API_MAC_CARBON
static Point
mac_get_ideal_size (f)
struct frame *f;
{
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
WindowRef w = FRAME_MAC_WINDOW (f);
Point ideal_size;
Rect standard_rect;
int height, width, columns, rows;
ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS);
ideal_size.v = dpyinfo->height;
IsWindowInStandardState (w, &ideal_size, &standard_rect);
/* Adjust the standard size according to character boundaries. */
width = standard_rect.right - standard_rect.left;
height = standard_rect.bottom - standard_rect.top;
columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width);
rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height);
ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, columns);
ideal_size.v = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows);
return ideal_size;
}
#endif
/* Handle clicks in zoom box. Calculation of "standard state" based
on code in IM - Window Manager A and code contributed by Ben
Mesander. The standard state of an Emacs window is 80-characters
wide (DEFAULT_NUM_COLS) and as tall as will fit on the screen. */
static void
do_zoom_window (WindowRef w, int zoom_in_or_out)
{
Rect zoom_rect, port_rect;
int width, height;
struct frame *f = mac_window_to_frame (w);
#if TARGET_API_MAC_CARBON
Point ideal_size = mac_get_ideal_size (f);
GetWindowBounds (w, kWindowContentRgn, &port_rect);
if (IsWindowInStandardState (w, &ideal_size, &zoom_rect)
&& port_rect.left == zoom_rect.left
&& port_rect.top == zoom_rect.top)
zoom_in_or_out = inZoomIn;
else
zoom_in_or_out = inZoomOut;
#ifdef MAC_OS8
mac_clear_window (f);
#endif
ZoomWindowIdeal (w, zoom_in_or_out, &ideal_size);
#else /* not TARGET_API_MAC_CARBON */
GrafPtr save_port;
Point top_left;
int w_title_height, rows;
struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
GetPort (&save_port);
SetPortWindowPort (w);
/* Clear window to avoid flicker. */
EraseRect (&(w->portRect));
if (zoom_in_or_out == inZoomOut)
{
SetPt (&top_left, w->portRect.left, w->portRect.top);
LocalToGlobal (&top_left);
/* calculate height of window's title bar */
w_title_height = top_left.v - 1
- (**((WindowPeek) w)->strucRgn).rgnBBox.top + GetMBarHeight ();
/* get maximum height of window into zoom_rect.bottom - zoom_rect.top */
zoom_rect = qd.screenBits.bounds;
zoom_rect.top += w_title_height;
InsetRect (&zoom_rect, 8, 4); /* not too tight */
zoom_rect.right = zoom_rect.left
+ FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS);
/* Adjust the standard size according to character boundaries. */
rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, zoom_rect.bottom - zoom_rect.top);
zoom_rect.bottom =
zoom_rect.top + FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows);
(**((WStateDataHandle) ((WindowPeek) w)->dataHandle)).stdState
= zoom_rect;
}
ZoomWindow (w, zoom_in_or_out, f == mac_focus_frame (dpyinfo));
SetPort (save_port);
#endif /* not TARGET_API_MAC_CARBON */
#if !TARGET_API_MAC_CARBON
/* retrieve window size and update application values */
port_rect = w->portRect;
height = port_rect.bottom - port_rect.top;
width = port_rect.right - port_rect.left;
mac_handle_size_change (f, width, height);
mac_handle_origin_change (f);
#endif
}
static void
mac_set_unicode_keystroke_event (code, buf)
UniChar code;
struct input_event *buf;
{
int charset_id, c1, c2;
if (code < 0x80)
{
buf->kind = ASCII_KEYSTROKE_EVENT;
buf->code = code;
}
else if (code < 0x100)
{
if (code < 0xA0)
charset_id = CHARSET_8_BIT_CONTROL;
else
charset_id = charset_latin_iso8859_1;
buf->kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
buf->code = MAKE_CHAR (charset_id, code, 0);
}
else
{
if (code < 0x2500)
charset_id = charset_mule_unicode_0100_24ff,
code -= 0x100;
else if (code < 0x33FF)
charset_id = charset_mule_unicode_2500_33ff,
code -= 0x2500;
else if (code >= 0xE000)
charset_id = charset_mule_unicode_e000_ffff,
code -= 0xE000;
c1 = (code / 96) + 32, c2 = (code % 96) + 32;
buf->kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
buf->code = MAKE_CHAR (charset_id, c1, c2);
}
}
static void
do_keystroke (action, char_code, key_code, modifiers, timestamp, buf)
EventKind action;
unsigned char char_code;
UInt32 key_code, modifiers;
unsigned long timestamp;
struct input_event *buf;
{
static SInt16 last_key_script = -1;
SInt16 current_key_script = GetScriptManagerVariable (smKeyScript);
UInt32 mapped_modifiers = mac_mapped_modifiers (modifiers);
#ifdef MAC_OSX
if (mapped_modifiers & kEventKeyModifierFnMask
&& key_code <= 0x7f
&& fn_keycode_to_keycode_table[key_code])
key_code = fn_keycode_to_keycode_table[key_code];
#endif
if (key_code <= 0x7f && keycode_to_xkeysym_table[key_code])
{
buf->kind = NON_ASCII_KEYSTROKE_EVENT;
buf->code = 0xff00 | keycode_to_xkeysym_table[key_code];
#ifdef MAC_OSX
if (modifiers & kEventKeyModifierFnMask
&& key_code <= 0x7f
&& fn_keycode_to_keycode_table[key_code] == key_code)
modifiers &= ~kEventKeyModifierFnMask;
#endif
}
else if (mapped_modifiers)
{
/* translate the keycode back to determine the original key */
#ifdef MAC_OSX
UCKeyboardLayout *uchr_ptr = NULL;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
OSStatus err;
KeyboardLayoutRef layout;
err = KLGetCurrentKeyboardLayout (&layout);
if (err == noErr)
err = KLGetKeyboardLayoutProperty (layout, kKLuchrData,
(const void **) &uchr_ptr);
#else
static SInt16 last_key_layout_id = 0;
static Handle uchr_handle = (Handle)-1;
SInt16 current_key_layout_id =
GetScriptVariable (current_key_script, smScriptKeys);
if (uchr_handle == (Handle)-1
|| last_key_layout_id != current_key_layout_id)
{
uchr_handle = GetResource ('uchr', current_key_layout_id);
last_key_layout_id = current_key_layout_id;
}
if (uchr_handle)
uchr_ptr = (UCKeyboardLayout *)*uchr_handle;
#endif
if (uchr_ptr)
{
OSStatus status;
UInt16 key_action = action - keyDown;
UInt32 modifier_key_state = (modifiers & ~mapped_modifiers) >> 8;
UInt32 keyboard_type = LMGetKbdType ();
SInt32 dead_key_state = 0;
UniChar code;
UniCharCount actual_length;
status = UCKeyTranslate (uchr_ptr, key_code, key_action,
modifier_key_state, keyboard_type,
kUCKeyTranslateNoDeadKeysMask,
&dead_key_state,
1, &actual_length, &code);
if (status == noErr && actual_length == 1)
mac_set_unicode_keystroke_event (code, buf);
}
#endif /* MAC_OSX */
if (buf->kind == NO_EVENT)
{
/* This code comes from Keyboard Resource, Appendix C of IM
- Text. This is necessary since shift is ignored in KCHR
table translation when option or command is pressed. It
also does not translate correctly control-shift chars
like C-% so mask off shift here also. */
/* Mask off modifier keys that are mapped to some Emacs
modifiers. */
int new_modifiers = modifiers & ~mapped_modifiers;
/* set high byte of keycode to modifier high byte*/
int new_key_code = key_code | new_modifiers;
Ptr kchr_ptr = (Ptr) GetScriptManagerVariable (smKCHRCache);
unsigned long some_state = 0;
UInt32 new_char_code;
new_char_code = KeyTranslate (kchr_ptr, new_key_code, &some_state);
if (new_char_code == 0)
/* Seems like a dead key. Append up-stroke. */
new_char_code = KeyTranslate (kchr_ptr, new_key_code | 0x80,
&some_state);
if (new_char_code)
{
buf->kind = ASCII_KEYSTROKE_EVENT;
buf->code = new_char_code & 0xff;
}
}
}
if (buf->kind == NO_EVENT)
{
buf->kind = ASCII_KEYSTROKE_EVENT;
buf->code = char_code;
}
buf->modifiers = mac_to_emacs_modifiers (modifiers);
buf->modifiers |= (extra_keyboard_modifiers
& (meta_modifier | alt_modifier
| hyper_modifier | super_modifier));
#if TARGET_API_MAC_CARBON
if (buf->kind == ASCII_KEYSTROKE_EVENT
&& buf->code >= 0x80 && buf->modifiers)
{
OSStatus err;
TextEncoding encoding = kTextEncodingMacRoman;
TextToUnicodeInfo ttu_info;
UpgradeScriptInfoToTextEncoding (current_key_script,
kTextLanguageDontCare,
kTextRegionDontCare,
NULL, &encoding);
err = CreateTextToUnicodeInfoByEncoding (encoding, &ttu_info);
if (err == noErr)
{
UniChar code;
Str255 pstr;
ByteCount unicode_len;
pstr[0] = 1;
pstr[1] = buf->code;
err = ConvertFromPStringToUnicode (ttu_info, pstr,
sizeof (UniChar),
&unicode_len, &code);
if (err == noErr && unicode_len == sizeof (UniChar))
mac_set_unicode_keystroke_event (code, buf);
DisposeTextToUnicodeInfo (&ttu_info);
}
}
#endif
if (buf->kind == ASCII_KEYSTROKE_EVENT
&& buf->code >= 0x80
&& last_key_script != current_key_script)
{
struct input_event event;
EVENT_INIT (event);
event.kind = LANGUAGE_CHANGE_EVENT;
event.arg = Qnil;
event.code = current_key_script;
event.timestamp = timestamp;
kbd_buffer_store_event (&event);
last_key_script = current_key_script;
}
}
void
mac_store_apple_event (class, id, desc)
Lisp_Object class, id;
const AEDesc *desc;
{
struct input_event buf;
EVENT_INIT (buf);
buf.kind = MAC_APPLE_EVENT;
buf.x = class;
buf.y = id;
XSETFRAME (buf.frame_or_window,
mac_focus_frame (&one_mac_display_info));
/* Now that Lisp object allocations are protected by BLOCK_INPUT, it
is safe to use them during read_socket_hook. */
buf.arg = mac_aedesc_to_lisp (desc);
kbd_buffer_store_event (&buf);
}
#if TARGET_API_MAC_CARBON
static OSStatus
mac_store_event_ref_as_apple_event (class, id, class_key, id_key,
event, num_params, names, types)
AEEventClass class;
AEEventID id;
Lisp_Object class_key, id_key;
EventRef event;
UInt32 num_params;
const EventParamName *names;
const EventParamType *types;
{
OSStatus err = eventNotHandledErr;
Lisp_Object binding;
mac_find_apple_event_spec (class, id, &class_key, &id_key, &binding);
if (!NILP (binding) && !EQ (binding, Qundefined))
{
if (INTEGERP (binding))
err = XINT (binding);
else
{
AppleEvent apple_event;
err = create_apple_event_from_event_ref (event, num_params,
names, types,
&apple_event);
if (err == noErr)
{
mac_store_apple_event (class_key, id_key, &apple_event);
AEDisposeDesc (&apple_event);
mac_wakeup_from_rne ();
}
}
}
return err;
}
void
mac_store_drag_event (window, mouse_pos, modifiers, desc)
WindowRef window;
Point mouse_pos;
SInt16 modifiers;
const AEDesc *desc;
{
struct input_event buf;
EVENT_INIT (buf);
buf.kind = DRAG_N_DROP_EVENT;
buf.modifiers = mac_to_emacs_modifiers (modifiers);
buf.timestamp = TickCount () * (1000 / 60);
XSETINT (buf.x, mouse_pos.h);
XSETINT (buf.y, mouse_pos.v);
XSETFRAME (buf.frame_or_window, mac_window_to_frame (window));
buf.arg = mac_aedesc_to_lisp (desc);
kbd_buffer_store_event (&buf);
}
#ifdef MAC_OSX
OSStatus
mac_store_service_event (event)
EventRef event;
{
OSStatus err;
Lisp_Object id_key;
int num_params;
const EventParamName *names;
const EventParamType *types;
static const EventParamName names_pfm[] =
{kEventParamServiceMessageName, kEventParamServiceUserData};
static const EventParamType types_pfm[] =
{typeCFStringRef, typeCFStringRef};
switch (GetEventKind (event))
{
case kEventServicePaste:
id_key = Qpaste;
num_params = 0;
names = NULL;
types = NULL;
break;
case kEventServicePerform:
id_key = Qperform;
num_params = sizeof (names_pfm) / sizeof (names_pfm[0]);
names = names_pfm;
types = types_pfm;
break;
default:
abort ();
}
err = mac_store_event_ref_as_apple_event (0, 0, Qservice, id_key,
event, num_params,
names, types);
return err;
}
#endif /* MAC_OSX */
static pascal OSStatus
mac_handle_window_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
WindowRef wp;
OSStatus err, result = eventNotHandledErr;
struct frame *f;
UInt32 attributes;
XSizeHints *size_hints;
err = GetEventParameter (event, kEventParamDirectObject, typeWindowRef,
NULL, sizeof (WindowRef), NULL, &wp);
if (err != noErr)
return eventNotHandledErr;
f = mac_window_to_frame (wp);
switch (GetEventKind (event))
{
/* -- window refresh events -- */
case kEventWindowUpdate:
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr)
break;
do_window_update (wp);
result = noErr;
break;
/* -- window state change events -- */
case kEventWindowShowing:
size_hints = FRAME_SIZE_HINTS (f);
if (!(size_hints->flags & (USPosition | PPosition)))
{
struct frame *sf = SELECTED_FRAME ();
if (!(FRAME_MAC_P (sf) && sf->async_visible))
RepositionWindow (wp, NULL, kWindowCenterOnMainScreen);
else
{
RepositionWindow (wp, FRAME_MAC_WINDOW (sf),
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
kWindowCascadeStartAtParentWindowScreen
#else
kWindowCascadeOnParentWindowScreen
#endif
);
#if USE_MAC_TOOLBAR
/* This is a workaround. RepositionWindow fails to put
a window at the cascading position when its parent
window has a Carbon HIToolbar. */
if ((f->left_pos == sf->left_pos
&& f->top_pos == sf->top_pos)
|| (f->left_pos == sf->left_pos + 10 * 2
&& f->top_pos == sf->top_pos + 32 * 2))
MoveWindowStructure (wp, sf->left_pos + 10, sf->top_pos + 32);
#endif
}
result = noErr;
}
break;
case kEventWindowHiding:
/* Before unmapping the window, update the WM_SIZE_HINTS
property to claim that the current position of the window is
user-specified, rather than program-specified, so that when
the window is mapped again, it will be placed at the same
location, without forcing the user to position it by hand
again (they have already done that once for this window.) */
x_wm_set_size_hint (f, (long) 0, 1);
result = noErr;
break;
case kEventWindowShown:
case kEventWindowHidden:
case kEventWindowCollapsed:
case kEventWindowExpanded:
mac_handle_visibility_change (f);
result = noErr;
break;
case kEventWindowBoundsChanging:
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr)
break;
err = GetEventParameter (event, kEventParamAttributes, typeUInt32,
NULL, sizeof (UInt32), NULL, &attributes);
if (err != noErr)
break;
size_hints = FRAME_SIZE_HINTS (f);
if ((attributes & kWindowBoundsChangeUserResize)
&& ((size_hints->flags & (PResizeInc | PBaseSize | PMinSize))
== (PResizeInc | PBaseSize | PMinSize)))
{
Rect bounds;
int width, height;
err = GetEventParameter (event, kEventParamCurrentBounds,
typeQDRectangle, NULL, sizeof (Rect),
NULL, &bounds);
if (err != noErr)
break;
width = bounds.right - bounds.left;
height = bounds.bottom - bounds.top;
if (width < size_hints->min_width)
width = size_hints->min_width;
else
width = size_hints->base_width
+ (int) ((width - size_hints->base_width)
/ (float) size_hints->width_inc + .5)
* size_hints->width_inc;
if (height < size_hints->min_height)
height = size_hints->min_height;
else
height = size_hints->base_height
+ (int) ((height - size_hints->base_height)
/ (float) size_hints->height_inc + .5)
* size_hints->height_inc;
bounds.right = bounds.left + width;
bounds.bottom = bounds.top + height;
SetEventParameter (event, kEventParamCurrentBounds,
typeQDRectangle, sizeof (Rect), &bounds);
result = noErr;
}
break;
case kEventWindowBoundsChanged:
err = GetEventParameter (event, kEventParamAttributes, typeUInt32,
NULL, sizeof (UInt32), NULL, &attributes);
if (err != noErr)
break;
if (attributes & kWindowBoundsChangeSizeChanged)
{
Rect bounds;
err = GetEventParameter (event, kEventParamCurrentBounds,
typeQDRectangle, NULL, sizeof (Rect),
NULL, &bounds);
if (err == noErr)
{
int width, height;
width = bounds.right - bounds.left;
height = bounds.bottom - bounds.top;
mac_handle_size_change (f, width, height);
mac_wakeup_from_rne ();
}
}
if (attributes & kWindowBoundsChangeOriginChanged)
mac_handle_origin_change (f);
result = noErr;
break;
/* -- window action events -- */
case kEventWindowClose:
{
struct input_event buf;
EVENT_INIT (buf);
buf.kind = DELETE_WINDOW_EVENT;
XSETFRAME (buf.frame_or_window, f);
buf.arg = Qnil;
kbd_buffer_store_event (&buf);
}
result = noErr;
break;
case kEventWindowGetIdealSize:
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr)
break;
{
Point ideal_size = mac_get_ideal_size (f);
err = SetEventParameter (event, kEventParamDimensions,
typeQDPoint, sizeof (Point), &ideal_size);
if (err == noErr)
result = noErr;
}
break;
#ifdef MAC_OSX
case kEventWindowToolbarSwitchMode:
{
static const EventParamName names[] = {kEventParamDirectObject,
kEventParamWindowMouseLocation,
kEventParamKeyModifiers,
kEventParamMouseButton,
kEventParamClickCount,
kEventParamMouseChord};
static const EventParamType types[] = {typeWindowRef,
typeQDPoint,
typeUInt32,
typeMouseButton,
typeUInt32,
typeUInt32};
int num_params = sizeof (names) / sizeof (names[0]);
err = mac_store_event_ref_as_apple_event (0, 0,
Qwindow,
Qtoolbar_switch_mode,
event, num_params,
names, types);
}
if (err == noErr)
result = noErr;
break;
#endif
#if USE_MAC_TSM
/* -- window focus events -- */
case kEventWindowFocusAcquired:
err = mac_tsm_resume ();
if (err == noErr)
result = noErr;
break;
case kEventWindowFocusRelinquish:
err = mac_tsm_suspend ();
if (err == noErr)
result = noErr;
break;
#endif
default:
abort ();
}
return result;
}
static pascal OSStatus
mac_handle_application_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus err, result = eventNotHandledErr;
switch (GetEventKind (event))
{
#if USE_MAC_TSM
case kEventAppActivated:
err = mac_tsm_resume ();
break;
case kEventAppDeactivated:
err = mac_tsm_suspend ();
break;
#endif
default:
abort ();
}
if (err == noErr)
result = noErr;
return result;
}
static pascal OSStatus
mac_handle_keyboard_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus err, result = eventNotHandledErr;
UInt32 event_kind, key_code, modifiers;
unsigned char char_code;
event_kind = GetEventKind (event);
switch (event_kind)
{
case kEventRawKeyDown:
case kEventRawKeyRepeat:
case kEventRawKeyUp:
/* When using Carbon Events, we need to pass raw keyboard events
to the TSM ourselves. If TSM handles it, it will pass back
noErr, otherwise it will pass back "eventNotHandledErr" and
we can process it normally. */
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr)
break;
if (read_socket_inev == NULL)
break;
#if USE_MAC_TSM
if (read_socket_inev->kind != NO_EVENT)
{
result = noErr;
break;
}
#endif
if (event_kind == kEventRawKeyUp)
break;
err = GetEventParameter (event, kEventParamKeyMacCharCodes,
typeChar, NULL,
sizeof (char), NULL, &char_code);
if (err != noErr)
break;
err = GetEventParameter (event, kEventParamKeyCode,
typeUInt32, NULL,
sizeof (UInt32), NULL, &key_code);
if (err != noErr)
break;
err = GetEventParameter (event, kEventParamKeyModifiers,
typeUInt32, NULL,
sizeof (UInt32), NULL, &modifiers);
if (err != noErr)
break;
do_keystroke ((event_kind == kEventRawKeyDown ? keyDown : autoKey),
char_code, key_code, modifiers,
((unsigned long)
(GetEventTime (event) / kEventDurationMillisecond)),
read_socket_inev);
result = noErr;
break;
default:
abort ();
}
return result;
}
static pascal OSStatus
mac_handle_command_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus err, result = eventNotHandledErr;
HICommand command;
static const EventParamName names[] =
{kEventParamDirectObject, kEventParamKeyModifiers};
static const EventParamType types[] =
{typeHICommand, typeUInt32};
int num_params = sizeof (names) / sizeof (names[0]);
err = GetEventParameter (event, kEventParamDirectObject, typeHICommand,
NULL, sizeof (HICommand), NULL, &command);
if (err != noErr)
return eventNotHandledErr;
switch (GetEventKind (event))
{
case kEventCommandProcess:
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr)
break;
err = GetEventParameter (event, kEventParamDirectObject,
typeHICommand, NULL,
sizeof (HICommand), NULL, &command);
if (err != noErr || command.commandID == 0)
break;
/* A HI command event is mapped to an Apple event whose event
class symbol is `hi-command' and event ID is its command
ID. */
err = mac_store_event_ref_as_apple_event (0, command.commandID,
Qhi_command, Qnil,
event, num_params,
names, types);
if (err == noErr)
result = noErr;
break;
default:
abort ();
}
return result;
}
static pascal OSStatus
mac_handle_mouse_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus err, result = eventNotHandledErr;
switch (GetEventKind (event))
{
case kEventMouseWheelMoved:
{
WindowRef wp;
struct frame *f;
EventMouseWheelAxis axis;
SInt32 delta;
Point point;
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr || read_socket_inev == NULL)
break;
f = mac_focus_frame (&one_mac_display_info);
err = GetEventParameter (event, kEventParamWindowRef, typeWindowRef,
NULL, sizeof (WindowRef), NULL, &wp);
if (err != noErr
|| wp != FRAME_MAC_WINDOW (f))
break;
err = GetEventParameter (event, kEventParamMouseWheelAxis,
typeMouseWheelAxis, NULL,
sizeof (EventMouseWheelAxis), NULL, &axis);
if (err != noErr || axis != kEventMouseWheelAxisY)
break;
err = GetEventParameter (event, kEventParamMouseLocation,
typeQDPoint, NULL, sizeof (Point),
NULL, &point);
if (err != noErr)
break;
point.h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
point.v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
if (point.h < 0 || point.v < 0
|| EQ (window_from_coordinates (f, point.h, point.v, 0, 0, 0, 1),
f->tool_bar_window))
break;
err = GetEventParameter (event, kEventParamMouseWheelDelta,
typeSInt32, NULL, sizeof (SInt32),
NULL, &delta);
if (err != noErr)
break;
read_socket_inev->kind = WHEEL_EVENT;
read_socket_inev->code = 0;
read_socket_inev->modifiers =
(mac_event_to_emacs_modifiers (event)
| ((delta < 0) ? down_modifier : up_modifier));
XSETINT (read_socket_inev->x, point.h);
XSETINT (read_socket_inev->y, point.v);
XSETFRAME (read_socket_inev->frame_or_window, f);
result = noErr;
}
break;
default:
abort ();
}
return result;
}
#if USE_MAC_TSM
static pascal OSStatus
mac_handle_text_input_event (next_handler, event, data)
EventHandlerCallRef next_handler;
EventRef event;
void *data;
{
OSStatus err, result;
Lisp_Object id_key = Qnil;
int num_params;
const EventParamName *names;
const EventParamType *types;
static UInt32 seqno_uaia = 0;
static const EventParamName names_uaia[] =
{kEventParamTextInputSendComponentInstance,
kEventParamTextInputSendRefCon,
kEventParamTextInputSendSLRec,
kEventParamTextInputSendFixLen,
kEventParamTextInputSendText,
kEventParamTextInputSendUpdateRng,
kEventParamTextInputSendHiliteRng,
kEventParamTextInputSendClauseRng,
kEventParamTextInputSendPinRng,
kEventParamTextInputSendTextServiceEncoding,
kEventParamTextInputSendTextServiceMacEncoding,
EVENT_PARAM_TEXT_INPUT_SEQUENCE_NUMBER};
static const EventParamType types_uaia[] =
{typeComponentInstance,
typeLongInteger,
typeIntlWritingCode,
typeLongInteger,
#ifdef MAC_OSX
typeUnicodeText,
#else
typeChar,
#endif
typeTextRangeArray,
typeTextRangeArray,
typeOffsetArray,
typeTextRange,
typeUInt32,
typeUInt32,
typeUInt32};
static const EventParamName names_ufke[] =
{kEventParamTextInputSendComponentInstance,
kEventParamTextInputSendRefCon,
kEventParamTextInputSendSLRec,
kEventParamTextInputSendText};
static const EventParamType types_ufke[] =
{typeComponentInstance,
typeLongInteger,
typeIntlWritingCode,
typeUnicodeText};
result = CallNextEventHandler (next_handler, event);
if (result != eventNotHandledErr)
return result;
switch (GetEventKind (event))
{
case kEventTextInputUpdateActiveInputArea:
id_key = Qupdate_active_input_area;
num_params = sizeof (names_uaia) / sizeof (names_uaia[0]);
names = names_uaia;
types = types_uaia;
SetEventParameter (event, EVENT_PARAM_TEXT_INPUT_SEQUENCE_NUMBER,
typeUInt32, sizeof (UInt32), &seqno_uaia);
seqno_uaia++;
result = noErr;
break;
case kEventTextInputUnicodeForKeyEvent:
{
EventRef kbd_event;
UInt32 actual_size, modifiers;
err = GetEventParameter (event, kEventParamTextInputSendKeyboardEvent,
typeEventRef, NULL, sizeof (EventRef), NULL,
&kbd_event);
if (err == noErr)
err = GetEventParameter (kbd_event, kEventParamKeyModifiers,
typeUInt32, NULL,
sizeof (UInt32), NULL, &modifiers);
if (err == noErr && mac_mapped_modifiers (modifiers))
/* There're mapped modifier keys. Process it in
do_keystroke. */
break;
if (err == noErr)
err = GetEventParameter (kbd_event, kEventParamKeyUnicodes,
typeUnicodeText, NULL, 0, &actual_size,
NULL);
if (err == noErr && actual_size == sizeof (UniChar))
{
UniChar code;
err = GetEventParameter (kbd_event, kEventParamKeyUnicodes,
typeUnicodeText, NULL,
sizeof (UniChar), NULL, &code);
if (err == noErr && code < 0x80)
{
/* ASCII character. Process it in do_keystroke. */
if (read_socket_inev && code >= 0x20 && code <= 0x7e)
{
UInt32 key_code;
err = GetEventParameter (kbd_event, kEventParamKeyCode,
typeUInt32, NULL, sizeof (UInt32),
NULL, &key_code);
if (!(err == noErr && key_code <= 0x7f
&& keycode_to_xkeysym_table [key_code]))
{
struct frame *f =
mac_focus_frame (&one_mac_display_info);
read_socket_inev->kind = ASCII_KEYSTROKE_EVENT;
read_socket_inev->code = code;
read_socket_inev->modifiers =
mac_to_emacs_modifiers (modifiers);
read_socket_inev->modifiers |=
(extra_keyboard_modifiers
& (meta_modifier | alt_modifier
| hyper_modifier | super_modifier));
XSETFRAME (read_socket_inev->frame_or_window, f);
}
}
break;
}
}
if (err == noErr)
{
/* Non-ASCII keystrokes without mapped modifiers are
processed at the Lisp level. */
id_key = Qunicode_for_key_event;
num_params = sizeof (names_ufke) / sizeof (names_ufke[0]);
names = names_ufke;
types = types_ufke;
result = noErr;
}
}
break;
case kEventTextInputOffsetToPos:
{
struct frame *f;
struct window *w;
Point p;
if (!OVERLAYP (Vmac_ts_active_input_overlay))
break;
/* Strictly speaking, this is not always correct because
previous events may change some states about display. */
if (!NILP (Foverlay_get (Vmac_ts_active_input_overlay, Qbefore_string)))
{
/* Active input area is displayed around the current point. */
f = SELECTED_FRAME ();
w = XWINDOW (f->selected_window);
}
else if (WINDOWP (echo_area_window))
{
/* Active input area is displayed in the echo area. */
w = XWINDOW (echo_area_window);
f = WINDOW_XFRAME (w);
}
else
break;
p.h = (WINDOW_TO_FRAME_PIXEL_X (w, w->cursor.x)
+ WINDOW_LEFT_FRINGE_WIDTH (w)
+ f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f));
p.v = (WINDOW_TO_FRAME_PIXEL_Y (w, w->cursor.y)
+ FONT_BASE (FRAME_FONT (f))
+ f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f));
err = SetEventParameter (event, kEventParamTextInputReplyPoint,
typeQDPoint, sizeof (typeQDPoint), &p);
if (err == noErr)
result = noErr;
}
break;
default:
abort ();
}
if (!NILP (id_key))
err = mac_store_event_ref_as_apple_event (0, 0, Qtext_input, id_key,
event, num_params,
names, types);
return result;
}
#endif
#endif /* TARGET_API_MAC_CARBON */
OSStatus
install_window_handler (window)
WindowRef window;
{
OSStatus err = noErr;
#if TARGET_API_MAC_CARBON
if (err == noErr)
{
static const EventTypeSpec specs[] =
{
/* -- window refresh events -- */
{kEventClassWindow, kEventWindowUpdate},
/* -- window state change events -- */
{kEventClassWindow, kEventWindowShowing},
{kEventClassWindow, kEventWindowHiding},
{kEventClassWindow, kEventWindowShown},
{kEventClassWindow, kEventWindowHidden},
{kEventClassWindow, kEventWindowCollapsed},
{kEventClassWindow, kEventWindowExpanded},
{kEventClassWindow, kEventWindowBoundsChanging},
{kEventClassWindow, kEventWindowBoundsChanged},
/* -- window action events -- */
{kEventClassWindow, kEventWindowClose},
{kEventClassWindow, kEventWindowGetIdealSize},
#ifdef MAC_OSX
{kEventClassWindow, kEventWindowToolbarSwitchMode},
#endif
#if USE_MAC_TSM
/* -- window focus events -- */
{kEventClassWindow, kEventWindowFocusAcquired},
{kEventClassWindow, kEventWindowFocusRelinquish},
#endif
};
static EventHandlerUPP handle_window_eventUPP = NULL;
if (handle_window_eventUPP == NULL)
handle_window_eventUPP = NewEventHandlerUPP (mac_handle_window_event);
err = InstallWindowEventHandler (window, handle_window_eventUPP,
GetEventTypeCount (specs),
specs, NULL, NULL);
}
#endif
if (err == noErr)
err = install_drag_handler (window);
return err;
}
void
remove_window_handler (window)
WindowRef window;
{
remove_drag_handler (window);
}
#if TARGET_API_MAC_CARBON
static OSStatus
install_application_handler ()
{
OSStatus err = noErr;
if (err == noErr)
{
static const EventTypeSpec specs[] = {
#if USE_MAC_TSM
{kEventClassApplication, kEventAppActivated},
{kEventClassApplication, kEventAppDeactivated},
#endif
};
err = InstallApplicationEventHandler (NewEventHandlerUPP
(mac_handle_application_event),
GetEventTypeCount (specs),
specs, NULL, NULL);
}
if (err == noErr)
{
static const EventTypeSpec specs[] =
{{kEventClassKeyboard, kEventRawKeyDown},
{kEventClassKeyboard, kEventRawKeyRepeat},
{kEventClassKeyboard, kEventRawKeyUp}};
err = InstallApplicationEventHandler (NewEventHandlerUPP
(mac_handle_keyboard_event),
GetEventTypeCount (specs),
specs, NULL, NULL);
}
if (err == noErr)
{
static const EventTypeSpec specs[] =
{{kEventClassCommand, kEventCommandProcess}};
err = InstallApplicationEventHandler (NewEventHandlerUPP
(mac_handle_command_event),
GetEventTypeCount (specs),
specs, NULL, NULL);
}
if (err == noErr)
{
static const EventTypeSpec specs[] =
{{kEventClassMouse, kEventMouseWheelMoved}};
err = InstallApplicationEventHandler (NewEventHandlerUPP
(mac_handle_mouse_event),
GetEventTypeCount (specs),
specs, NULL, NULL);
}
#if USE_MAC_TSM
if (err == noErr)
{
static const EventTypeSpec spec[] =
{{kEventClassTextInput, kEventTextInputUpdateActiveInputArea},
{kEventClassTextInput, kEventTextInputUnicodeForKeyEvent},
{kEventClassTextInput, kEventTextInputOffsetToPos}};
err = InstallApplicationEventHandler (NewEventHandlerUPP
(mac_handle_text_input_event),
GetEventTypeCount (spec),
spec, NULL, NULL);
}
#endif
if (err == noErr)
err = install_menu_target_item_handler ();
#ifdef MAC_OSX
if (err == noErr)
err = install_service_handler ();
#endif
return err;
}
#endif
static pascal void
mac_handle_dm_notification (event)
AppleEvent *event;
{
mac_screen_config_changed = 1;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
static void
mac_handle_cg_display_reconfig (display, flags, user_info)
CGDirectDisplayID display;
CGDisplayChangeSummaryFlags flags;
void *user_info;
{
mac_screen_config_changed = 1;
}
#endif
static OSErr
init_dm_notification_handler ()
{
OSErr err = noErr;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
if (CGDisplayRegisterReconfigurationCallback != NULL)
#endif
{
CGDisplayRegisterReconfigurationCallback (mac_handle_cg_display_reconfig,
NULL);
}
#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
else /* CGDisplayRegisterReconfigurationCallback == NULL */
#endif
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1030 || MAC_OS_X_VERSION_MIN_REQUIRED == 1020
{
static DMNotificationUPP handle_dm_notificationUPP = NULL;
ProcessSerialNumber psn;
if (handle_dm_notificationUPP == NULL)
handle_dm_notificationUPP =
NewDMNotificationUPP (mac_handle_dm_notification);
err = GetCurrentProcess (&psn);
if (err == noErr)
err = DMRegisterNotifyProc (handle_dm_notificationUPP, &psn);
}
#endif
return err;
}
static void
mac_get_screen_info (dpyinfo)
struct mac_display_info *dpyinfo;
{
#ifdef MAC_OSX
/* HasDepth returns true if it is possible to have a 32 bit display,
but this may not be what is actually used. Mac OSX can do better. */
dpyinfo->color_p = CGDisplaySamplesPerPixel (kCGDirectMainDisplay) > 1;
dpyinfo->n_planes = CGDisplayBitsPerPixel (kCGDirectMainDisplay);
{
CGDisplayErr err;
CGDisplayCount ndisps;
CGDirectDisplayID *displays;
err = CGGetActiveDisplayList (0, NULL, &ndisps);
if (err == noErr)
{
displays = alloca (sizeof (CGDirectDisplayID) * ndisps);
err = CGGetActiveDisplayList (ndisps, displays, &ndisps);
}
if (err == noErr)
{
CGRect bounds = CGRectZero;
while (ndisps-- > 0)
bounds = CGRectUnion (bounds, CGDisplayBounds (displays[ndisps]));
dpyinfo->height = CGRectGetHeight (bounds);
dpyinfo->width = CGRectGetWidth (bounds);
}
else
{
dpyinfo->height = CGDisplayPixelsHigh (kCGDirectMainDisplay);
dpyinfo->width = CGDisplayPixelsWide (kCGDirectMainDisplay);
}
}
#else /* !MAC_OSX */
{
GDHandle gdh = GetMainDevice ();
Rect rect = (**gdh).gdRect;
dpyinfo->color_p = TestDeviceAttribute (gdh, gdDevType);
for (dpyinfo->n_planes = 32; dpyinfo->n_planes > 0; dpyinfo->n_planes >>= 1)
if (HasDepth (gdh, dpyinfo->n_planes, gdDevType, dpyinfo->color_p))
break;
for (gdh = DMGetFirstScreenDevice (dmOnlyActiveDisplays); gdh;
gdh = DMGetNextScreenDevice (gdh, dmOnlyActiveDisplays))
UnionRect (&rect, &(**gdh).gdRect, &rect);
dpyinfo->height = rect.bottom - rect.top;
dpyinfo->width = rect.right - rect.left;
}
#endif /* !MAC_OSX */
}
#if __profile__
void
profiler_exit_proc ()
{
ProfilerDump ("\pEmacs.prof");
ProfilerTerm ();
}
#endif
/* These few functions implement Emacs as a normal Mac application
(almost): set up the heap and the Toolbox, handle necessary system
events plus a few simple menu events. They also set up Emacs's
access to functions defined in the rest of this file. Emacs uses
function hooks to perform all its terminal I/O. A complete list of
these functions appear in termhooks.h. For what they do, read the
comments there and see also w32term.c and xterm.c. What's
noticeably missing here is the event loop, which is normally
present in most Mac application. After performing the necessary
Mac initializations, main passes off control to emacs_main
(corresponding to main in emacs.c). Emacs_main calls XTread_socket
(defined further below) to read input. This is where
WaitNextEvent/ReceiveNextEvent is called to process Mac events. */
#ifdef MAC_OS8
#undef main
int
main (void)
{
#if __profile__ /* is the profiler on? */
if (ProfilerInit(collectDetailed, bestTimeBase, 5000, 200))
exit(1);
#endif
#if __MWERKS__
/* set creator and type for files created by MSL */
_fcreator = MAC_EMACS_CREATOR_CODE;
_ftype = 'TEXT';
#endif
do_init_managers ();
do_get_menus ();
#ifndef USE_LSB_TAG
do_check_ram_size ();
#endif
init_emacs_passwd_dir ();
init_environ ();
init_coercion_handler ();
initialize_applescript ();
init_apple_event_handler ();
init_dm_notification_handler ();
{
char **argv;
int argc = 0;
/* set up argv array from STR# resource */
get_string_list (&argv, ARGV_STRING_LIST_ID);
while (argv[argc])
argc++;
/* free up AppleScript resources on exit */
atexit (terminate_applescript);
#if __profile__ /* is the profiler on? */
atexit (profiler_exit_proc);
#endif
/* 3rd param "envp" never used in emacs_main */
(void) emacs_main (argc, argv, 0);
}
/* Never reached - real exit in Fkill_emacs */
return 0;
}
#endif
#if !TARGET_API_MAC_CARBON
static RgnHandle mouse_region = NULL;
Boolean
mac_wait_next_event (er, sleep_time, dequeue)
EventRecord *er;
UInt32 sleep_time;
Boolean dequeue;
{
static EventRecord er_buf = {nullEvent};
UInt32 target_tick, current_tick;
EventMask event_mask;
if (mouse_region == NULL)
mouse_region = NewRgn ();
event_mask = everyEvent;
if (!mac_ready_for_apple_events)
event_mask -= highLevelEventMask;
current_tick = TickCount ();
target_tick = current_tick + sleep_time;
if (er_buf.what == nullEvent)
while (!WaitNextEvent (event_mask, &er_buf,
target_tick - current_tick, mouse_region))
{
current_tick = TickCount ();
if (target_tick <= current_tick)
return false;
}
*er = er_buf;
if (dequeue)
er_buf.what = nullEvent;
return true;
}
#endif /* not TARGET_API_MAC_CARBON */
#if TARGET_API_MAC_CARBON
OSStatus
mac_post_mouse_moved_event ()
{
EventRef event = NULL;
OSStatus err;
err = CreateEvent (NULL, kEventClassMouse, kEventMouseMoved, 0,
kEventAttributeNone, &event);
if (err == noErr)
{
Point mouse_pos;
GetGlobalMouse (&mouse_pos);
err = SetEventParameter (event, kEventParamMouseLocation, typeQDPoint,
sizeof (Point), &mouse_pos);
}
if (err == noErr)
{
UInt32 modifiers = GetCurrentKeyModifiers ();
err = SetEventParameter (event, kEventParamKeyModifiers, typeUInt32,
sizeof (UInt32), &modifiers);
}
if (err == noErr)
err = PostEventToQueue (GetCurrentEventQueue (), event,
kEventPriorityStandard);
if (event)
ReleaseEvent (event);
return err;
}
#endif
/* Emacs calls this whenever it wants to read an input event from the
user. */
int
XTread_socket (sd, expected, hold_quit)
int sd, expected;
struct input_event *hold_quit;
{
struct input_event inev;
int count = 0;
#if TARGET_API_MAC_CARBON
EventRef eventRef;
EventTargetRef toolbox_dispatcher;
#endif
EventRecord er;
struct mac_display_info *dpyinfo = &one_mac_display_info;
if (interrupt_input_blocked)
{
interrupt_input_pending = 1;
return -1;
}
interrupt_input_pending = 0;
BLOCK_INPUT;
/* So people can tell when we have read the available input. */
input_signal_count++;
++handling_signal;
#if TARGET_API_MAC_CARBON
toolbox_dispatcher = GetEventDispatcherTarget ();
while (
#if USE_CG_DRAWING
mac_prepare_for_quickdraw (NULL),
#endif
!ReceiveNextEvent (0, NULL, kEventDurationNoWait,
kEventRemoveFromQueue, &eventRef))
#else /* !TARGET_API_MAC_CARBON */
while (mac_wait_next_event (&er, 0, true))
#endif /* !TARGET_API_MAC_CARBON */
{
int do_help = 0;
struct frame *f;
unsigned long timestamp;
EVENT_INIT (inev);
inev.kind = NO_EVENT;
inev.arg = Qnil;
#if TARGET_API_MAC_CARBON
timestamp = GetEventTime (eventRef) / kEventDurationMillisecond;
if (!mac_convert_event_ref (eventRef, &er))
goto OTHER;
#else /* !TARGET_API_MAC_CARBON */
timestamp = er.when * (1000 / 60); /* ticks to milliseconds */
#endif /* !TARGET_API_MAC_CARBON */
switch (er.what)
{
case mouseDown:
case mouseUp:
{
WindowRef window_ptr;
ControlPartCode part_code;
int tool_bar_p = 0;
#if TARGET_API_MAC_CARBON
OSStatus err;
/* This is needed to send mouse events like aqua window
buttons to the correct handler. */
read_socket_inev = &inev;
err = SendEventToEventTarget (eventRef, toolbox_dispatcher);
read_socket_inev = NULL;
if (err != eventNotHandledErr)
break;
#endif
last_mouse_glyph_frame = 0;
if (dpyinfo->grabbed && last_mouse_frame
&& FRAME_LIVE_P (last_mouse_frame))
{
window_ptr = FRAME_MAC_WINDOW (last_mouse_frame);
part_code = inContent;
}
else
{
part_code = FindWindow (er.where, &window_ptr);
if (tip_window && window_ptr == tip_window)
{
HideWindow (tip_window);
part_code = FindWindow (er.where, &window_ptr);
}
}
if (er.what != mouseDown &&
(part_code != inContent || dpyinfo->grabbed == 0))
break;
switch (part_code)
{
case inMenuBar:
f = mac_focus_frame (dpyinfo);
saved_menu_event_location = er.where;
inev.kind = MENU_BAR_ACTIVATE_EVENT;
XSETFRAME (inev.frame_or_window, f);
break;
case inContent:
if (
#if TARGET_API_MAC_CARBON
FrontNonFloatingWindow ()
#else
FrontWindow ()
#endif
!= window_ptr
|| (mac_window_to_frame (window_ptr)
!= dpyinfo->x_focus_frame))
SelectWindow (window_ptr);
else
{
ControlPartCode control_part_code;
ControlRef ch;
Point mouse_loc;
#ifdef MAC_OSX
ControlKind control_kind;
#endif
f = mac_window_to_frame (window_ptr);
/* convert to local coordinates of new window */
mouse_loc.h = (er.where.h
- (f->left_pos
+ FRAME_OUTER_TO_INNER_DIFF_X (f)));
mouse_loc.v = (er.where.v
- (f->top_pos
+ FRAME_OUTER_TO_INNER_DIFF_Y (f)));
#if TARGET_API_MAC_CARBON
ch = FindControlUnderMouse (mouse_loc, window_ptr,
&control_part_code);
#ifdef MAC_OSX
if (ch)
GetControlKind (ch, &control_kind);
#endif
#else
control_part_code = FindControl (mouse_loc, window_ptr,
&ch);
#endif
#if TARGET_API_MAC_CARBON
inev.code = mac_get_mouse_btn (eventRef);
inev.modifiers = mac_event_to_emacs_modifiers (eventRef);
#else
inev.code = mac_get_emulated_btn (er.modifiers);
inev.modifiers = mac_to_emacs_modifiers (er.modifiers);
#endif
XSETINT (inev.x, mouse_loc.h);
XSETINT (inev.y, mouse_loc.v);
if ((dpyinfo->grabbed && tracked_scroll_bar)
|| (ch != 0
#ifndef USE_TOOLKIT_SCROLL_BARS
/* control_part_code becomes kControlNoPart if
a progress indicator is clicked. */
&& control_part_code != kControlNoPart
#else /* USE_TOOLKIT_SCROLL_BARS */
#ifdef MAC_OSX
&& control_kind.kind == kControlKindScrollBar
#endif /* MAC_OSX */
#endif /* USE_TOOLKIT_SCROLL_BARS */
))
{
struct scroll_bar *bar;
if (dpyinfo->grabbed && tracked_scroll_bar)
{
bar = tracked_scroll_bar;
#ifndef USE_TOOLKIT_SCROLL_BARS
control_part_code = kControlIndicatorPart;
#endif
}
else
bar = (struct scroll_bar *) GetControlReference (ch);
#ifdef USE_TOOLKIT_SCROLL_BARS
/* Make the "Ctrl-Mouse-2 splits window" work
for toolkit scroll bars. */
if (inev.modifiers & ctrl_modifier)
x_scroll_bar_handle_click (bar, control_part_code,
&er, &inev);
else if (er.what == mouseDown)
x_scroll_bar_handle_press (bar, control_part_code,
mouse_loc, &inev);
else
x_scroll_bar_handle_release (bar, &inev);
#else /* not USE_TOOLKIT_SCROLL_BARS */
x_scroll_bar_handle_click (bar, control_part_code,
&er, &inev);
if (er.what == mouseDown
&& control_part_code == kControlIndicatorPart)
tracked_scroll_bar = bar;
else
tracked_scroll_bar = NULL;
#endif /* not USE_TOOLKIT_SCROLL_BARS */
}
else
{
Lisp_Object window;
int x = mouse_loc.h;
int y = mouse_loc.v;
window = window_from_coordinates (f, x, y, 0, 0, 0, 1);
if (EQ (window, f->tool_bar_window))
{
if (er.what == mouseDown)
handle_tool_bar_click (f, x, y, 1, 0);
else
handle_tool_bar_click (f, x, y, 0,
inev.modifiers);
tool_bar_p = 1;
}
else
{
XSETFRAME (inev.frame_or_window, f);
inev.kind = MOUSE_CLICK_EVENT;
}
}
if (er.what == mouseDown)
{
dpyinfo->grabbed |= (1 << inev.code);
last_mouse_frame = f;
if (!tool_bar_p)
last_tool_bar_item = -1;
}
else
{
if ((dpyinfo->grabbed & (1 << inev.code)) == 0)
/* If a button is released though it was not
previously pressed, that would be because
of multi-button emulation. */
dpyinfo->grabbed = 0;
else
dpyinfo->grabbed &= ~(1 << inev.code);
}
/* Ignore any mouse motion that happened before
this event; any subsequent mouse-movement Emacs
events should reflect only motion after the
ButtonPress. */
if (f != 0)
f->mouse_moved = 0;
#ifdef USE_TOOLKIT_SCROLL_BARS
if (inev.kind == MOUSE_CLICK_EVENT
|| (inev.kind == SCROLL_BAR_CLICK_EVENT
&& (inev.modifiers & ctrl_modifier)))
#endif
switch (er.what)
{
case mouseDown:
inev.modifiers |= down_modifier;
break;
case mouseUp:
inev.modifiers |= up_modifier;
break;
}
}
break;
case inDrag:
#if TARGET_API_MAC_CARBON
case inProxyIcon:
if (IsWindowPathSelectClick (window_ptr, &er))
{
WindowPathSelect (window_ptr, NULL, NULL);
break;
}
if (part_code == inProxyIcon
&& (TrackWindowProxyDrag (window_ptr, er.where)
!= errUserWantsToDragWindow))
break;
DragWindow (window_ptr, er.where, NULL);
#else /* not TARGET_API_MAC_CARBON */
DragWindow (window_ptr, er.where, &qd.screenBits.bounds);
/* Update the frame parameters. */
{
struct frame *f = mac_window_to_frame (window_ptr);
if (f && !f->async_iconified)
mac_handle_origin_change (f);
}
#endif /* not TARGET_API_MAC_CARBON */
break;
case inGoAway:
if (TrackGoAway (window_ptr, er.where))
{
inev.kind = DELETE_WINDOW_EVENT;
XSETFRAME (inev.frame_or_window,
mac_window_to_frame (window_ptr));
}
break;
/* window resize handling added --ben */
case inGrow:
do_grow_window (window_ptr, &er);
break;
/* window zoom handling added --ben */
case inZoomIn:
case inZoomOut:
if (TrackBox (window_ptr, er.where, part_code))
do_zoom_window (window_ptr, part_code);
break;
#if USE_MAC_TOOLBAR
case inStructure:
{
OSStatus err;
HIViewRef ch;
err = HIViewGetViewForMouseEvent (HIViewGetRoot (window_ptr),
eventRef, &ch);
/* This doesn't work on Mac OS X 10.2. */
if (err == noErr)
HIViewClick (ch, eventRef);
}
break;
#endif /* USE_MAC_TOOLBAR */
default:
break;
}
}
break;
#if !TARGET_API_MAC_CARBON
case updateEvt:
do_window_update ((WindowRef) er.message);
break;
#endif
case osEvt:
switch ((er.message >> 24) & 0x000000FF)
{
case mouseMovedMessage:
#if !TARGET_API_MAC_CARBON
SetRectRgn (mouse_region, er.where.h, er.where.v,
er.where.h + 1, er.where.v + 1);
#endif
previous_help_echo_string = help_echo_string;
help_echo_string = Qnil;
if (dpyinfo->grabbed && last_mouse_frame
&& FRAME_LIVE_P (last_mouse_frame))
f = last_mouse_frame;
else
f = dpyinfo->x_focus_frame;
if (dpyinfo->mouse_face_hidden)
{
dpyinfo->mouse_face_hidden = 0;
clear_mouse_face (dpyinfo);
}
if (f)
{
WindowRef wp = FRAME_MAC_WINDOW (f);
Point mouse_pos;
mouse_pos.h = (er.where.h
- (f->left_pos
+ FRAME_OUTER_TO_INNER_DIFF_X (f)));
mouse_pos.v = (er.where.v
- (f->top_pos
+ FRAME_OUTER_TO_INNER_DIFF_Y (f)));
if (dpyinfo->grabbed && tracked_scroll_bar)
#ifdef USE_TOOLKIT_SCROLL_BARS
x_scroll_bar_handle_drag (wp, tracked_scroll_bar,
mouse_pos, &inev);
#else /* not USE_TOOLKIT_SCROLL_BARS */
x_scroll_bar_note_movement (tracked_scroll_bar,
mouse_pos.v
- XINT (tracked_scroll_bar->top),
er.when * (1000 / 60));
#endif /* not USE_TOOLKIT_SCROLL_BARS */
else
{
/* Generate SELECT_WINDOW_EVENTs when needed. */
if (!NILP (Vmouse_autoselect_window))
{
Lisp_Object window;
window = window_from_coordinates (f,
mouse_pos.h,
mouse_pos.v,
0, 0, 0, 0);
/* Window will be selected only when it is
not selected now and last mouse movement
event was not in it. Minibuffer window
will be selected only when it is active. */
if (WINDOWP (window)
&& !EQ (window, last_window)
&& !EQ (window, selected_window)
/* For click-to-focus window managers
create event iff we don't leave the
selected frame. */
&& (focus_follows_mouse
|| (EQ (XWINDOW (window)->frame,
XWINDOW (selected_window)->frame))))
{
inev.kind = SELECT_WINDOW_EVENT;
inev.frame_or_window = window;
}
last_window=window;
}
if (!note_mouse_movement (f, &mouse_pos))
help_echo_string = previous_help_echo_string;
#if USE_MAC_TOOLBAR
else
mac_tool_bar_note_mouse_movement (f, eventRef);
#endif
}
}
/* If the contents of the global variable
help_echo_string has changed, generate a
HELP_EVENT. */
if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
do_help = 1;
break;
default:
goto OTHER;
}
break;
case activateEvt:
{
WindowRef window_ptr = (WindowRef) er.message;
OSErr err;
ControlRef root_control;
if (window_ptr == tip_window)
{
HideWindow (tip_window);
break;
}
if (!is_emacs_window (window_ptr))
goto OTHER;
f = mac_window_to_frame (window_ptr);
if ((er.modifiers & activeFlag) != 0)
{
/* A window has been activated */
Point mouse_loc;
err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control);
if (err == noErr)
ActivateControl (root_control);
x_detect_focus_change (dpyinfo, &er, &inev);
mouse_loc.h = (er.where.h
- (f->left_pos
+ FRAME_OUTER_TO_INNER_DIFF_X (f)));
mouse_loc.v = (er.where.v
- (f->top_pos
+ FRAME_OUTER_TO_INNER_DIFF_Y (f)));
/* Window-activated event counts as mouse movement,
so update things that depend on mouse position. */
note_mouse_movement (f, &mouse_loc);
}
else
{
/* A window has been deactivated */
err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control);
if (err == noErr)
DeactivateControl (root_control);
#ifdef USE_TOOLKIT_SCROLL_BARS
if (dpyinfo->grabbed && tracked_scroll_bar)
{
struct input_event event;
EVENT_INIT (event);
event.kind = NO_EVENT;
x_scroll_bar_handle_release (tracked_scroll_bar, &event);
if (event.kind != NO_EVENT)
{
event.timestamp = timestamp;
kbd_buffer_store_event_hold (&event, hold_quit);
count++;
}
}
#endif
dpyinfo->grabbed = 0;
x_detect_focus_change (dpyinfo, &er, &inev);
if (f == dpyinfo->mouse_face_mouse_frame)
{
/* If we move outside the frame, then we're
certainly no longer on any text in the
frame. */
clear_mouse_face (dpyinfo);
dpyinfo->mouse_face_mouse_frame = 0;
}
/* Generate a nil HELP_EVENT to cancel a help-echo.
Do it only if there's something to cancel.
Otherwise, the startup message is cleared when the
mouse leaves the frame. */
if (any_help_event_p)
do_help = -1;
}
}
break;
case keyDown:
case keyUp:
case autoKey:
ObscureCursor ();
f = mac_focus_frame (dpyinfo);
XSETFRAME (inev.frame_or_window, f);
/* If mouse-highlight is an integer, input clears out mouse
highlighting. */
if (!dpyinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight)
&& !EQ (f->tool_bar_window, dpyinfo->mouse_face_window))
{
clear_mouse_face (dpyinfo);
dpyinfo->mouse_face_hidden = 1;
}
{
UInt32 modifiers = er.modifiers, mapped_modifiers;
#ifdef MAC_OSX
GetEventParameter (eventRef, kEventParamKeyModifiers,
typeUInt32, NULL,
sizeof (UInt32), NULL, &modifiers);
#endif
mapped_modifiers = mac_mapped_modifiers (modifiers);
#if TARGET_API_MAC_CARBON
if (!(mapped_modifiers
& ~(mac_pass_command_to_system ? cmdKey : 0)
& ~(mac_pass_control_to_system ? controlKey : 0)))
goto OTHER;
else
#endif
if (er.what != keyUp)
do_keystroke (er.what, er.message & charCodeMask,
(er.message & keyCodeMask) >> 8,
modifiers, timestamp, &inev);
}
break;
case kHighLevelEvent:
AEProcessAppleEvent (&er);
break;
default:
OTHER:
#if TARGET_API_MAC_CARBON
{
OSStatus err;
read_socket_inev = &inev;
err = SendEventToEventTarget (eventRef, toolbox_dispatcher);
read_socket_inev = NULL;
}
#endif
break;
}
#if TARGET_API_MAC_CARBON
ReleaseEvent (eventRef);
#endif
if (inev.kind != NO_EVENT)
{
inev.timestamp = timestamp;
kbd_buffer_store_event_hold (&inev, hold_quit);
count++;
}
if (do_help
&& !(hold_quit && hold_quit->kind != NO_EVENT))
{
Lisp_Object frame;
if (f)
XSETFRAME (frame, f);
else
frame = Qnil;
if (do_help > 0)
{
any_help_event_p = 1;
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);
}
count++;
}
}
/* If the focus was just given to an autoraising frame,
raise it now. */
/* ??? This ought to be able to handle more than one such frame. */
if (pending_autoraise_frame)
{
x_raise_frame (pending_autoraise_frame);
pending_autoraise_frame = 0;
}
if (mac_screen_config_changed)
{
mac_get_screen_info (dpyinfo);
mac_screen_config_changed = 0;
}
#if !TARGET_API_MAC_CARBON
/* Check which frames are still visible. We do this here because
there doesn't seem to be any direct notification from the Window
Manager that the visibility of a window has changed (at least,
not in all cases). */
{
Lisp_Object tail, frame;
FOR_EACH_FRAME (tail, frame)
{
struct frame *f = XFRAME (frame);
/* The tooltip has been drawn already. Avoid the
SET_FRAME_GARBAGED in mac_handle_visibility_change. */
if (EQ (frame, tip_frame))
continue;
if (FRAME_MAC_P (f))
mac_handle_visibility_change (f);
}
}
#endif
--handling_signal;
UNBLOCK_INPUT;
return count;
}
/* Need to override CodeWarrior's input function so no conversion is
done on newlines Otherwise compiled functions in .elc files will be
read incorrectly. Defined in ...:MSL C:MSL
Common:Source:buffer_io.c. */
#ifdef __MWERKS__
void
__convert_to_newlines (unsigned char * p, size_t * n)
{
#pragma unused(p,n)
}
void
__convert_from_newlines (unsigned char * p, size_t * n)
{
#pragma unused(p,n)
}
#endif
#ifdef MAC_OS8
void
make_mac_terminal_frame (struct frame *f)
{
Lisp_Object frame;
Rect r;
XSETFRAME (frame, f);
f->output_method = output_mac;
f->output_data.mac = (struct mac_output *)
xmalloc (sizeof (struct mac_output));
bzero (f->output_data.mac, sizeof (struct mac_output));
XSETFRAME (FRAME_KBOARD (f)->Vdefault_minibuffer_frame, f);
FRAME_COLS (f) = 96;
FRAME_LINES (f) = 4;
FRAME_CAN_HAVE_SCROLL_BARS (f) = 1;
FRAME_VERTICAL_SCROLL_BAR_TYPE (f) = vertical_scroll_bar_right;
FRAME_DESIRED_CURSOR (f) = FILLED_BOX_CURSOR;
f->output_data.mac->cursor_pixel = 0;
f->output_data.mac->border_pixel = 0x00ff00;
f->output_data.mac->mouse_pixel = 0xff00ff;
f->output_data.mac->cursor_foreground_pixel = 0x0000ff;
f->output_data.mac->text_cursor = kThemeIBeamCursor;
f->output_data.mac->nontext_cursor = kThemeArrowCursor;
f->output_data.mac->modeline_cursor = kThemeArrowCursor;
f->output_data.mac->hand_cursor = kThemePointingHandCursor;
f->output_data.mac->hourglass_cursor = kThemeWatchCursor;
f->output_data.mac->horizontal_drag_cursor = kThemeResizeLeftRightCursor;
FRAME_FONTSET (f) = -1;
f->output_data.mac->explicit_parent = 0;
f->left_pos = 8;
f->top_pos = 32;
f->border_width = 0;
f->internal_border_width = 0;
f->auto_raise = 1;
f->auto_lower = 1;
f->new_text_cols = 0;
f->new_text_lines = 0;
SetRect (&r, f->left_pos, f->top_pos,
f->left_pos + FRAME_PIXEL_WIDTH (f),
f->top_pos + FRAME_PIXEL_HEIGHT (f));
BLOCK_INPUT;
if (!(FRAME_MAC_WINDOW (f) =
NewCWindow (NULL, &r, "\p", true, dBoxProc,
(WindowRef) -1, 1, (long) f->output_data.mac)))
abort ();
/* so that update events can find this mac_output struct */
f->output_data.mac->mFP = f; /* point back to emacs frame */
UNBLOCK_INPUT;
x_make_gc (f);
/* Need to be initialized for unshow_buffer in window.c. */
selected_window = f->selected_window;
Fmodify_frame_parameters (frame,
Fcons (Fcons (Qfont,
build_string ("-*-monaco-medium-r-*--*-90-*-*-*-*-mac-roman")), Qnil));
Fmodify_frame_parameters (frame,
Fcons (Fcons (Qforeground_color,
build_string ("black")), Qnil));
Fmodify_frame_parameters (frame,
Fcons (Fcons (Qbackground_color,
build_string ("white")), Qnil));
}
#endif
/***********************************************************************
Initialization
***********************************************************************/
static int mac_initialized = 0;
static XrmDatabase
mac_make_rdb (xrm_option)
const char *xrm_option;
{
XrmDatabase database;
database = xrm_get_preference_database (NULL);
if (xrm_option)
xrm_merge_string_database (database, xrm_option);
return database;
}
struct mac_display_info *
mac_term_init (display_name, xrm_option, resource_name)
Lisp_Object display_name;
char *xrm_option;
char *resource_name;
{
struct mac_display_info *dpyinfo;
struct terminal *terminal;
BLOCK_INPUT;
if (!mac_initialized)
{
mac_initialize ();
mac_initialized = 1;
}
if (x_display_list)
error ("Sorry, this version can only handle one display");
dpyinfo = &one_mac_display_info;
bzero (dpyinfo, sizeof (*dpyinfo));
terminal = mac_create_terminal (dpyinfo);
/* 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;
#ifdef MAC_OSX
dpyinfo->mac_id_name
= (char *) xmalloc (SCHARS (Vinvocation_name)
+ SCHARS (Vsystem_name)
+ 2);
sprintf (dpyinfo->mac_id_name, "%s@%s",
SDATA (Vinvocation_name), SDATA (Vsystem_name));
#else
dpyinfo->mac_id_name = (char *) xmalloc (strlen ("Mac Display") + 1);
strcpy (dpyinfo->mac_id_name, "Mac Display");
#endif
dpyinfo->reference_count = 0;
dpyinfo->resx = 72.0;
dpyinfo->resy = 72.0;
mac_get_screen_info (dpyinfo);
dpyinfo->grabbed = 0;
dpyinfo->root_window = NULL;
dpyinfo->terminal->image_cache = make_image_cache ();
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 = Qnil;
dpyinfo->mouse_face_overlay = Qnil;
dpyinfo->mouse_face_hidden = 0;
dpyinfo->xrdb = mac_make_rdb (xrm_option);
/* Put this display on the chain. */
dpyinfo->next = x_display_list;
x_display_list = dpyinfo;
/* Put it on x_display_name_list. */
x_display_name_list = Fcons (Fcons (display_name,
Fcons (Qnil, dpyinfo->xrdb)),
x_display_name_list);
dpyinfo->name_list_element = XCAR (x_display_name_list);
/* FIXME: Untested.
Add the default keyboard. */
add_keyboard_wait_descriptor (0);
#if USE_CG_DRAWING
mac_init_fringe (terminal->rif);
#endif
UNBLOCK_INPUT;
return dpyinfo;
}
/* Get rid of display DPYINFO, assuming all frames are already gone. */
void
x_delete_display (dpyinfo)
struct mac_display_info *dpyinfo;
{
int i;
/* Discard this display from x_display_name_list and x_display_list.
We can't use Fdelq because that can quit. */
if (! NILP (x_display_name_list)
&& EQ (XCAR (x_display_name_list), dpyinfo->name_list_element))
x_display_name_list = XCDR (x_display_name_list);
else
{
Lisp_Object tail;
tail = x_display_name_list;
while (CONSP (tail) && CONSP (XCDR (tail)))
{
if (EQ (XCAR (XCDR (tail)), dpyinfo->name_list_element))
{
XSETCDR (tail, XCDR (XCDR (tail)));
break;
}
tail = XCDR (tail);
}
}
if (x_display_list == dpyinfo)
x_display_list = dpyinfo->next;
else
{
struct x_display_info *tail;
for (tail = x_display_list; tail; tail = tail->next)
if (tail->next == dpyinfo)
tail->next = tail->next->next;
}
/* Free the font names in the font table. */
for (i = 0; i < dpyinfo->n_fonts; i++)
if (dpyinfo->font_table[i].name)
{
if (dpyinfo->font_table[i].name != dpyinfo->font_table[i].full_name)
xfree (dpyinfo->font_table[i].full_name);
xfree (dpyinfo->font_table[i].name);
}
if (dpyinfo->font_table)
{
if (dpyinfo->font_table->font_encoder)
xfree (dpyinfo->font_table->font_encoder);
xfree (dpyinfo->font_table);
}
if (dpyinfo->mac_id_name)
xfree (dpyinfo->mac_id_name);
if (x_display_list == 0)
{
mac_clear_font_name_table ();
bzero (dpyinfo, sizeof (*dpyinfo));
}
}
static void
init_menu_bar ()
{
#ifdef MAC_OSX
OSStatus err;
MenuRef menu;
MenuItemIndex menu_index;
err = GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
&menu, &menu_index);
if (err == noErr)
SetMenuItemCommandKey (menu, menu_index, false, 0);
EnableMenuCommand (NULL, kHICommandPreferences);
err = GetIndMenuItemWithCommandID (NULL, kHICommandPreferences, 1,
&menu, &menu_index);
if (err == noErr)
{
SetMenuItemCommandKey (menu, menu_index, false, 0);
InsertMenuItemTextWithCFString (menu, NULL,
0, kMenuItemAttrSeparator, 0);
InsertMenuItemTextWithCFString (menu, CFSTR ("About Emacs"),
0, 0, kHICommandAbout);
}
#else /* !MAC_OSX */
#if TARGET_API_MAC_CARBON
SetMenuItemCommandID (GetMenuRef (M_APPLE), I_ABOUT, kHICommandAbout);
#endif
#endif
}
#if USE_MAC_TSM
static void
init_tsm ()
{
#ifdef MAC_OSX
static InterfaceTypeList types = {kUnicodeDocument};
#else
static InterfaceTypeList types = {kTextService};
#endif
NewTSMDocument (sizeof (types) / sizeof (types[0]), types,
&tsm_document_id, 0);
}
#endif
/* Set up use of X before we make the first connection. */
extern frame_parm_handler mac_frame_parm_handlers[];
static struct redisplay_interface x_redisplay_interface =
{
mac_frame_parm_handlers,
x_produce_glyphs,
x_write_glyphs,
x_insert_glyphs,
x_clear_end_of_line,
x_scroll_run,
x_after_update_window_line,
x_update_window_begin,
x_update_window_end,
x_cursor_to,
x_flush,
#if USE_CG_DRAWING
mac_flush_display_optional,
#else
0, /* flush_display_optional */
#endif
x_clear_window_mouse_face,
x_get_glyph_overhangs,
x_fix_overlapping_area,
x_draw_fringe_bitmap,
#if USE_CG_DRAWING
mac_define_fringe_bitmap,
mac_destroy_fringe_bitmap,
#else
0, /* define_fringe_bitmap */
0, /* destroy_fringe_bitmap */
#endif
mac_per_char_metric,
mac_encode_char,
mac_compute_glyph_string_overhangs,
x_draw_glyph_string,
mac_define_frame_cursor,
mac_clear_frame_area,
mac_draw_window_cursor,
mac_draw_vertical_window_border,
mac_shift_glyphs_for_insert
};
static struct terminal *
mac_create_terminal (struct mac_display_info *dpyinfo)
{
struct terminal *terminal;
terminal = create_terminal ();
terminal->type = output_mac;
terminal->display_info.mac = dpyinfo;
dpyinfo->terminal = terminal;
terminal->clear_frame_hook = x_clear_frame;
terminal->ins_del_lines_hook = x_ins_del_lines;
terminal->delete_glyphs_hook = x_delete_glyphs;
terminal->ring_bell_hook = XTring_bell;
terminal->reset_terminal_modes_hook = XTreset_terminal_modes;
terminal->set_terminal_modes_hook = XTset_terminal_modes;
terminal->update_begin_hook = x_update_begin;
terminal->update_end_hook = x_update_end;
terminal->set_terminal_window_hook = XTset_terminal_window;
terminal->read_socket_hook = XTread_socket;
terminal->frame_up_to_date_hook = XTframe_up_to_date;
terminal->mouse_position_hook = XTmouse_position;
terminal->frame_rehighlight_hook = XTframe_rehighlight;
terminal->frame_raise_lower_hook = XTframe_raise_lower;
/* terminal->fullscreen_hook = XTfullscreen_hook; */
terminal->set_vertical_scroll_bar_hook = XTset_vertical_scroll_bar;
terminal->condemn_scroll_bars_hook = XTcondemn_scroll_bars;
terminal->redeem_scroll_bar_hook = XTredeem_scroll_bar;
terminal->judge_scroll_bars_hook = XTjudge_scroll_bars;
terminal->delete_frame_hook = x_destroy_window;
/* terminal->delete_terminal_hook = x_delete_terminal; */
terminal->rif = &x_redisplay_interface;
#if 0
TTY_SCROLL_REGION_OK (CURTTY ()) = 1; /* we'll scroll partial frames */
TTY_CHAR_INS_DEL_OK (CURTTY ()) = 1;
TTY_LINE_INS_DEL_OK (CURTTY ()) = 1; /* we'll just blt 'em */
TTY_FAST_CLEAR_END_OF_LINE (CURTTY ()) = 1; /* X does this well */
TTY_MEMORY_BELOW_FRAME (CURTTY ()) = 0; /* we don't remember what
scrolls off the
bottom */
#else
terminal->scroll_region_ok = 1; /* We'll scroll partial frames. */
terminal->char_ins_del_ok = 1;
terminal->line_ins_del_ok = 1; /* We'll just blt 'em. */
terminal->fast_clear_end_of_line = 1; /* X does this well. */
terminal->memory_below_frame = 0; /* We don't remember what scrolls
off the bottom. */
#endif
/* FIXME: This keyboard setup is 100% untested, just copied from
w32_create_terminal in order to set window-system now that it's
a keyboard object. */
/* We don't yet support separate terminals on Mac, so don't try to share
keyboards between virtual terminals that are on the same physical
terminal like X does. */
terminal->kboard = (KBOARD *) xmalloc (sizeof (KBOARD));
init_kboard (terminal->kboard);
terminal->kboard->Vwindow_system = intern ("mac");
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++;
return terminal;
}
static void
mac_initialize ()
{
baud_rate = 19200;
last_tool_bar_item = -1;
any_help_event_p = 0;
/* Try to use interrupt input; if we can't, then start polling. */
Fset_input_interrupt_mode (Qt);
BLOCK_INPUT;
#if TARGET_API_MAC_CARBON
install_application_handler ();
init_menu_bar ();
#if USE_MAC_TSM
init_tsm ();
#endif
#ifdef MAC_OSX
init_coercion_handler ();
init_apple_event_handler ();
init_dm_notification_handler ();
if (!inhibit_window_system)
{
static const ProcessSerialNumber psn = {0, kCurrentProcess};
SetFrontProcess (&psn);
}
#endif
#endif
#if USE_CG_DRAWING
init_cg_color ();
#endif
UNBLOCK_INPUT;
}
void
syms_of_macterm ()
{
#if 0
staticpro (&x_error_message_string);
x_error_message_string = Qnil;
#endif
Qcontrol = intern ("control"); staticpro (&Qcontrol);
Qmeta = intern ("meta"); staticpro (&Qmeta);
Qalt = intern ("alt"); staticpro (&Qalt);
Qhyper = intern ("hyper"); staticpro (&Qhyper);
Qsuper = intern ("super"); staticpro (&Qsuper);
Qmodifier_value = intern ("modifier-value");
staticpro (&Qmodifier_value);
Fput (Qcontrol, Qmodifier_value, make_number (ctrl_modifier));
Fput (Qmeta, Qmodifier_value, make_number (meta_modifier));
Fput (Qalt, Qmodifier_value, make_number (alt_modifier));
Fput (Qhyper, Qmodifier_value, make_number (hyper_modifier));
Fput (Qsuper, Qmodifier_value, make_number (super_modifier));
#if TARGET_API_MAC_CARBON
Qhi_command = intern ("hi-command"); staticpro (&Qhi_command);
#ifdef MAC_OSX
Qtoolbar_switch_mode = intern ("toolbar-switch-mode");
staticpro (&Qtoolbar_switch_mode);
#if USE_MAC_FONT_PANEL
Qpanel_closed = intern ("panel-closed"); staticpro (&Qpanel_closed);
Qselection = intern ("selection"); staticpro (&Qselection);
#endif
Qservice = intern ("service"); staticpro (&Qservice);
Qpaste = intern ("paste"); staticpro (&Qpaste);
Qperform = intern ("perform"); staticpro (&Qperform);
Qmouse_drag_overlay = intern ("mouse-drag-overlay");
staticpro (&Qmouse_drag_overlay);
#endif
#if USE_MAC_TSM
Qtext_input = intern ("text-input"); staticpro (&Qtext_input);
Qupdate_active_input_area = intern ("update-active-input-area");
staticpro (&Qupdate_active_input_area);
Qunicode_for_key_event = intern ("unicode-for-key-event");
staticpro (&Qunicode_for_key_event);
#endif
#endif
#ifdef MAC_OSX
Fprovide (intern ("mac-carbon"), Qnil);
#endif
staticpro (&Qreverse);
Qreverse = intern ("reverse");
staticpro (&x_display_name_list);
x_display_name_list = Qnil;
staticpro (&last_mouse_scroll_bar);
last_mouse_scroll_bar = Qnil;
staticpro (&fm_font_family_alist);
fm_font_family_alist = Qnil;
#if USE_ATSUI
staticpro (&atsu_font_id_hash);
atsu_font_id_hash = Qnil;
staticpro (&fm_style_face_attributes_alist);
fm_style_face_attributes_alist = Qnil;
#endif
#if USE_MAC_TSM
staticpro (&saved_ts_script_language_on_focus);
saved_ts_script_language_on_focus = Qnil;
#endif
/* We don't yet support this, but defining this here avoids whining
from cus-start.el and other places, like "M-x set-variable". */
DEFVAR_BOOL ("x-use-underline-position-properties",
&x_use_underline_position_properties,
doc: /* *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: /* *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;
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
staticpro (&last_mouse_motion_frame);
last_mouse_motion_frame = Qnil;
/* Variables to configure modifier key assignment. */
DEFVAR_LISP ("mac-control-modifier", &Vmac_control_modifier,
doc: /* *Modifier key assumed when the Mac control key is pressed.
The value can be `control', `meta', `alt', `hyper', or `super' for the
respective modifier. The default is `control'. */);
Vmac_control_modifier = Qcontrol;
DEFVAR_LISP ("mac-option-modifier", &Vmac_option_modifier,
doc: /* *Modifier key assumed when the Mac alt/option key is pressed.
The value can be `control', `meta', `alt', `hyper', or `super' for the
respective modifier. If the value is nil then the key will act as the
normal Mac control modifier, and the option key can be used to compose
characters depending on the chosen Mac keyboard setting. */);
Vmac_option_modifier = Qnil;
DEFVAR_LISP ("mac-command-modifier", &Vmac_command_modifier,
doc: /* *Modifier key assumed when the Mac command key is pressed.
The value can be `control', `meta', `alt', `hyper', or `super' for the
respective modifier. The default is `meta'. */);
Vmac_command_modifier = Qmeta;
DEFVAR_LISP ("mac-function-modifier", &Vmac_function_modifier,
doc: /* *Modifier key assumed when the Mac function key is pressed.
The value can be `control', `meta', `alt', `hyper', or `super' for the
respective modifier. Note that remapping the function key may lead to
unexpected results for some keys on non-US/GB keyboards. */);
Vmac_function_modifier = Qnil;
DEFVAR_LISP ("mac-emulate-three-button-mouse",
&Vmac_emulate_three_button_mouse,
doc: /* *Specify a way of three button mouse emulation.
The value can be nil, t, or the symbol `reverse'.
A value of nil means that no emulation should be done and the modifiers
should be placed on the mouse-1 event.
t means that when the option-key is held down while pressing the mouse
button, the click will register as mouse-2 and while the command-key
is held down, the click will register as mouse-3.
The symbol `reverse' means that the option-key will register for
mouse-3 and the command-key will register for mouse-2. */);
Vmac_emulate_three_button_mouse = Qnil;
#if TARGET_API_MAC_CARBON
DEFVAR_BOOL ("mac-wheel-button-is-mouse-2", &mac_wheel_button_is_mouse_2,
doc: /* *Non-nil if the wheel button is mouse-2 and the right click mouse-3.
Otherwise, the right click will be treated as mouse-2 and the wheel
button will be mouse-3. */);
mac_wheel_button_is_mouse_2 = 1;
DEFVAR_BOOL ("mac-pass-command-to-system", &mac_pass_command_to_system,
doc: /* *Non-nil if command key presses are passed on to the Mac Toolbox. */);
mac_pass_command_to_system = 1;
DEFVAR_BOOL ("mac-pass-control-to-system", &mac_pass_control_to_system,
doc: /* *Non-nil if control key presses are passed on to the Mac Toolbox. */);
mac_pass_control_to_system = 1;
#endif
DEFVAR_BOOL ("mac-allow-anti-aliasing", &mac_use_core_graphics,
doc: /* *If non-nil, allow anti-aliasing.
The text will be rendered using Core Graphics text rendering which
may anti-alias the text. */);
#if USE_CG_DRAWING
mac_use_core_graphics = 1;
#else
mac_use_core_graphics = 0;
#endif
/* Register an entry for `mac-roman' so that it can be used when
creating the terminal frame on Mac OS 9 before loading
term/mac-win.elc. */
DEFVAR_LISP ("mac-charset-info-alist", &Vmac_charset_info_alist,
doc: /* Alist of Emacs character sets vs text encodings and coding systems.
Each entry should be of the form:
(CHARSET-NAME TEXT-ENCODING CODING-SYSTEM)
where CHARSET-NAME is a string used in font names to identify the
charset, TEXT-ENCODING is a TextEncodingBase value in Mac, and
CODING_SYSTEM is a coding system corresponding to TEXT-ENCODING. */);
Vmac_charset_info_alist =
Fcons (list3 (build_string ("mac-roman"),
make_number (smRoman), Qnil), Qnil);
#if USE_MAC_TSM
DEFVAR_LISP ("mac-ts-active-input-overlay", &Vmac_ts_active_input_overlay,
doc: /* Overlay used to display Mac TSM active input area. */);
Vmac_ts_active_input_overlay = Qnil;
DEFVAR_LISP ("mac-ts-active-input-buf", &Vmac_ts_active_input_buf,
doc: /* Byte sequence of the current Mac TSM active input area. */);
/* `empty_string' is not ready yet on Mac OS Classic. */
Vmac_ts_active_input_buf = build_string ("");
DEFVAR_LISP ("mac-ts-script-language-on-focus", &Vmac_ts_script_language_on_focus,
doc: /* *How to change Mac TSM script/language when a frame gets focus.
If the value is t, the input script and language are restored to those
used in the last focus frame. If the value is a pair of integers, the
input script and language codes, which are defined in the Script
Manager, are set to its car and cdr parts, respectively. Otherwise,
Emacs doesn't set them and thus follows the system default behavior. */);
Vmac_ts_script_language_on_focus = Qnil;
#endif
}
/* arch-tag: f2259165-4454-4c04-a029-a133c8af7b5b
(do not change this comment) */