diff options
| author | Alan Third | 2019-02-10 10:59:29 +0000 |
|---|---|---|
| committer | Alan Third | 2019-12-30 11:22:17 +0000 |
| commit | 53f3d1f9f41e291f50977bbcff1d43416972208e (patch) | |
| tree | 302db920b0cc9f5bab9b18e55e6daffab9cb13e3 | |
| parent | f01286675ad7ff520138ca2a42a9bf3a45740b54 (diff) | |
| download | emacs-scratch/ns/draw-to-bitmap.tar.gz emacs-scratch/ns/draw-to-bitmap.zip | |
Draw to offscreen buffer on macOSscratch/ns/draw-to-bitmap
* src/nsfns.m (x_set_background_color): Clear the frame after changing
the background color, not before.
* src/nsterm.h (drawingBuffer): New variable.
([EmacsView focusOnDrawingBuffer]):
([EmacsView copyRect:to:]):
([EmacsView createDrawingBufferWithRect:]): New methods.
* src/nsterm.m (ns_update_begin):
(ns_update_end):
(ns_focus):
(ns_unfocus): Handle drawing to offscreen buffer.
(ns_clip_to_row): Use ns_row_rect.
(ns_copy_bits): Remove unused function.
(ns_scroll_run):
(ns_shift_glyphs_for_insert): Use new scrolling method.
(ns_draw_fringe_bitmap):
(ns_dumpglyphs_image): When drawing to the offscreen buffer, flip
images so they appear the right way up.
(ns_dumpglyphs_stretch): Remove unnecessary code.
(ns_draw_window_cursor): Don't disable screen updates.
([EmacsView updateFrameSize:]): Update the size of the offscreen
buffer.
([EmacsView initFrameFromEmacs:]): Create offscreen buffer.
([EmacsView windowDidChangeBackingProperties:]):
([EmacsView createDrawingBufferWithRect:]):
([EmacsView focusOnDrawingBuffer]):
([EmacsView copyRect]): New methods.
([EmacsView viewWillDraw]): Remove method as it no longer does
anything useful.
([EmacsView drawRect:]): Handle drawing from offscreen buffer.
| -rw-r--r-- | src/nsfns.m | 11 | ||||
| -rw-r--r-- | src/nsterm.h | 11 | ||||
| -rw-r--r-- | src/nsterm.m | 478 |
3 files changed, 248 insertions, 252 deletions
diff --git a/src/nsfns.m b/src/nsfns.m index 3e835a71d03..4ae73de6754 100644 --- a/src/nsfns.m +++ b/src/nsfns.m | |||
| @@ -287,12 +287,6 @@ ns_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) | |||
| 287 | error ("Unknown color"); | 287 | error ("Unknown color"); |
| 288 | } | 288 | } |
| 289 | 289 | ||
| 290 | /* Clear the frame; in some instances the NS-internal GC appears not | ||
| 291 | to update, or it does update and cannot clear old text | ||
| 292 | properly. */ | ||
| 293 | if (FRAME_VISIBLE_P (f)) | ||
| 294 | ns_clear_frame (f); | ||
| 295 | |||
| 296 | [col retain]; | 290 | [col retain]; |
| 297 | [f->output_data.ns->background_color release]; | 291 | [f->output_data.ns->background_color release]; |
| 298 | f->output_data.ns->background_color = col; | 292 | f->output_data.ns->background_color = col; |
| @@ -324,7 +318,10 @@ ns_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) | |||
| 324 | } | 318 | } |
| 325 | 319 | ||
| 326 | if (FRAME_VISIBLE_P (f)) | 320 | if (FRAME_VISIBLE_P (f)) |
| 327 | SET_FRAME_GARBAGED (f); | 321 | { |
| 322 | SET_FRAME_GARBAGED (f); | ||
| 323 | ns_clear_frame (f); | ||
| 324 | } | ||
| 328 | } | 325 | } |
| 329 | unblock_input (); | 326 | unblock_input (); |
| 330 | } | 327 | } |
diff --git a/src/nsterm.h b/src/nsterm.h index 3803009afa6..e9394e184fd 100644 --- a/src/nsterm.h +++ b/src/nsterm.h | |||
| @@ -417,6 +417,9 @@ typedef id instancetype; | |||
| 417 | int maximized_width, maximized_height; | 417 | int maximized_width, maximized_height; |
| 418 | NSWindow *nonfs_window; | 418 | NSWindow *nonfs_window; |
| 419 | BOOL fs_is_native; | 419 | BOOL fs_is_native; |
| 420 | #ifdef NS_IMPL_COCOA | ||
| 421 | NSBitmapImageRep *drawingBuffer; | ||
| 422 | #endif | ||
| 420 | @public | 423 | @public |
| 421 | struct frame *emacsframe; | 424 | struct frame *emacsframe; |
| 422 | int rows, cols; | 425 | int rows, cols; |
| @@ -457,7 +460,13 @@ typedef id instancetype; | |||
| 457 | #endif | 460 | #endif |
| 458 | - (int)fullscreenState; | 461 | - (int)fullscreenState; |
| 459 | 462 | ||
| 460 | /* Non-notification versions of NSView methods. Used for direct calls. */ | 463 | #ifdef NS_IMPL_COCOA |
| 464 | - (void)focusOnDrawingBuffer; | ||
| 465 | #endif | ||
| 466 | - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect; | ||
| 467 | - (void)createDrawingBufferWithRect:(NSRect)rect; | ||
| 468 | |||
| 469 | /* Non-notification versions of NSView methods. Used for direct calls. */ | ||
| 461 | - (void)windowWillEnterFullScreen; | 470 | - (void)windowWillEnterFullScreen; |
| 462 | - (void)windowDidEnterFullScreen; | 471 | - (void)windowDidEnterFullScreen; |
| 463 | - (void)windowWillExitFullScreen; | 472 | - (void)windowWillExitFullScreen; |
diff --git a/src/nsterm.m b/src/nsterm.m index 2d76a260d8d..e643707394b 100644 --- a/src/nsterm.m +++ b/src/nsterm.m | |||
| @@ -290,9 +290,6 @@ long context_menu_value = 0; | |||
| 290 | static struct frame *ns_updating_frame; | 290 | static struct frame *ns_updating_frame; |
| 291 | static NSView *focus_view = NULL; | 291 | static NSView *focus_view = NULL; |
| 292 | static int ns_window_num = 0; | 292 | static int ns_window_num = 0; |
| 293 | #ifdef NS_IMPL_GNUSTEP | ||
| 294 | static NSRect uRect; // TODO: This is dead, remove it? | ||
| 295 | #endif | ||
| 296 | static BOOL gsaved = NO; | 293 | static BOOL gsaved = NO; |
| 297 | static BOOL ns_fake_keydown = NO; | 294 | static BOOL ns_fake_keydown = NO; |
| 298 | #ifdef NS_IMPL_COCOA | 295 | #ifdef NS_IMPL_COCOA |
| @@ -1120,33 +1117,10 @@ ns_update_begin (struct frame *f) | |||
| 1120 | #endif | 1117 | #endif |
| 1121 | 1118 | ||
| 1122 | ns_updating_frame = f; | 1119 | ns_updating_frame = f; |
| 1123 | [view lockFocus]; | ||
| 1124 | |||
| 1125 | /* drawRect may have been called for say the minibuffer, and then clip path | ||
| 1126 | is for the minibuffer. But the display engine may draw more because | ||
| 1127 | we have set the frame as garbaged. So reset clip path to the whole | ||
| 1128 | view. */ | ||
| 1129 | #ifdef NS_IMPL_COCOA | 1120 | #ifdef NS_IMPL_COCOA |
| 1130 | { | 1121 | [view focusOnDrawingBuffer]; |
| 1131 | NSBezierPath *bp; | 1122 | #else |
| 1132 | NSRect r = [view frame]; | 1123 | [view lockFocus]; |
| 1133 | NSRect cr = [[view window] frame]; | ||
| 1134 | /* If a large frame size is set, r may be larger than the window frame | ||
| 1135 | before constrained. In that case don't change the clip path, as we | ||
| 1136 | will clear in to the tool bar and title bar. */ | ||
| 1137 | if (r.size.height | ||
| 1138 | + FRAME_NS_TITLEBAR_HEIGHT (f) | ||
| 1139 | + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height) | ||
| 1140 | { | ||
| 1141 | bp = [[NSBezierPath bezierPathWithRect: r] retain]; | ||
| 1142 | [bp setClip]; | ||
| 1143 | [bp release]; | ||
| 1144 | } | ||
| 1145 | } | ||
| 1146 | #endif | ||
| 1147 | |||
| 1148 | #ifdef NS_IMPL_GNUSTEP | ||
| 1149 | uRect = NSMakeRect (0, 0, 0, 0); | ||
| 1150 | #endif | 1124 | #endif |
| 1151 | } | 1125 | } |
| 1152 | 1126 | ||
| @@ -1165,12 +1139,17 @@ ns_update_end (struct frame *f) | |||
| 1165 | /* if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */ | 1139 | /* if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */ |
| 1166 | MOUSE_HL_INFO (f)->mouse_face_defer = 0; | 1140 | MOUSE_HL_INFO (f)->mouse_face_defer = 0; |
| 1167 | 1141 | ||
| 1142 | #ifdef NS_IMPL_COCOA | ||
| 1143 | [NSGraphicsContext setCurrentContext:nil]; | ||
| 1144 | [view display]; | ||
| 1145 | #else | ||
| 1168 | block_input (); | 1146 | block_input (); |
| 1169 | 1147 | ||
| 1170 | [view unlockFocus]; | 1148 | [view unlockFocus]; |
| 1171 | [[view window] flushWindow]; | 1149 | [[view window] flushWindow]; |
| 1172 | 1150 | ||
| 1173 | unblock_input (); | 1151 | unblock_input (); |
| 1152 | #endif | ||
| 1174 | ns_updating_frame = NULL; | 1153 | ns_updating_frame = NULL; |
| 1175 | } | 1154 | } |
| 1176 | 1155 | ||
| @@ -1185,6 +1164,8 @@ ns_focus (struct frame *f, NSRect *r, int n) | |||
| 1185 | the entire window. | 1164 | the entire window. |
| 1186 | -------------------------------------------------------------------------- */ | 1165 | -------------------------------------------------------------------------- */ |
| 1187 | { | 1166 | { |
| 1167 | EmacsView *view = FRAME_NS_VIEW (f); | ||
| 1168 | |||
| 1188 | NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus"); | 1169 | NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus"); |
| 1189 | if (r != NULL) | 1170 | if (r != NULL) |
| 1190 | { | 1171 | { |
| @@ -1192,27 +1173,34 @@ ns_focus (struct frame *f, NSRect *r, int n) | |||
| 1192 | } | 1173 | } |
| 1193 | 1174 | ||
| 1194 | if (f != ns_updating_frame) | 1175 | if (f != ns_updating_frame) |
| 1176 | #ifdef NS_IMPL_COCOA | ||
| 1177 | [view focusOnDrawingBuffer]; | ||
| 1178 | #else | ||
| 1195 | { | 1179 | { |
| 1196 | NSView *view = FRAME_NS_VIEW (f); | ||
| 1197 | if (view != focus_view) | 1180 | if (view != focus_view) |
| 1198 | { | 1181 | { |
| 1199 | if (focus_view != NULL) | 1182 | if (focus_view != NULL) |
| 1200 | { | 1183 | { |
| 1201 | [focus_view unlockFocus]; | 1184 | [focus_view unlockFocus]; |
| 1202 | [[focus_view window] flushWindow]; | 1185 | [[focus_view window] flushWindow]; |
| 1203 | /*debug_lock--; */ | ||
| 1204 | } | 1186 | } |
| 1205 | 1187 | ||
| 1206 | if (view) | 1188 | if (view) |
| 1207 | [view lockFocus]; | 1189 | [view lockFocus]; |
| 1208 | focus_view = view; | 1190 | focus_view = view; |
| 1209 | /*if (view) debug_lock++; */ | ||
| 1210 | } | 1191 | } |
| 1211 | } | 1192 | } |
| 1193 | #endif | ||
| 1212 | 1194 | ||
| 1213 | /* clipping */ | 1195 | /* clipping */ |
| 1214 | if (r) | 1196 | if (r) |
| 1215 | { | 1197 | { |
| 1198 | #ifdef NS_IMPL_COCOA | ||
| 1199 | int i; | ||
| 1200 | for (i = 0 ; i < n ; i++) | ||
| 1201 | [view setNeedsDisplayInRect:r[i]]; | ||
| 1202 | #endif | ||
| 1203 | |||
| 1216 | [[NSGraphicsContext currentContext] saveGraphicsState]; | 1204 | [[NSGraphicsContext currentContext] saveGraphicsState]; |
| 1217 | if (n == 2) | 1205 | if (n == 2) |
| 1218 | NSRectClipList (r, 2); | 1206 | NSRectClipList (r, 2); |
| @@ -1237,6 +1225,7 @@ ns_unfocus (struct frame *f) | |||
| 1237 | gsaved = NO; | 1225 | gsaved = NO; |
| 1238 | } | 1226 | } |
| 1239 | 1227 | ||
| 1228 | #ifdef NS_IMPL_GNUSTEP | ||
| 1240 | if (f != ns_updating_frame) | 1229 | if (f != ns_updating_frame) |
| 1241 | { | 1230 | { |
| 1242 | if (focus_view != NULL) | 1231 | if (focus_view != NULL) |
| @@ -1244,9 +1233,9 @@ ns_unfocus (struct frame *f) | |||
| 1244 | [focus_view unlockFocus]; | 1233 | [focus_view unlockFocus]; |
| 1245 | [[focus_view window] flushWindow]; | 1234 | [[focus_view window] flushWindow]; |
| 1246 | focus_view = NULL; | 1235 | focus_view = NULL; |
| 1247 | /*debug_lock--; */ | ||
| 1248 | } | 1236 | } |
| 1249 | } | 1237 | } |
| 1238 | #endif | ||
| 1250 | } | 1239 | } |
| 1251 | 1240 | ||
| 1252 | 1241 | ||
| @@ -1258,16 +1247,7 @@ ns_clip_to_row (struct window *w, struct glyph_row *row, | |||
| 1258 | -------------------------------------------------------------------------- */ | 1247 | -------------------------------------------------------------------------- */ |
| 1259 | { | 1248 | { |
| 1260 | struct frame *f = XFRAME (WINDOW_FRAME (w)); | 1249 | struct frame *f = XFRAME (WINDOW_FRAME (w)); |
| 1261 | NSRect clip_rect; | 1250 | NSRect clip_rect = ns_row_rect (w, row, area); |
| 1262 | int window_x, window_y, window_width; | ||
| 1263 | |||
| 1264 | window_box (w, area, &window_x, &window_y, &window_width, 0); | ||
| 1265 | |||
| 1266 | clip_rect.origin.x = window_x; | ||
| 1267 | clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y)); | ||
| 1268 | clip_rect.origin.y = max (clip_rect.origin.y, window_y); | ||
| 1269 | clip_rect.size.width = window_width; | ||
| 1270 | clip_rect.size.height = row->visible_height; | ||
| 1271 | 1251 | ||
| 1272 | ns_focus (f, &clip_rect, 1); | 1252 | ns_focus (f, &clip_rect, 1); |
| 1273 | } | 1253 | } |
| @@ -2911,22 +2891,6 @@ ns_clear_frame_area (struct frame *f, int x, int y, int width, int height) | |||
| 2911 | return; | 2891 | return; |
| 2912 | } | 2892 | } |
| 2913 | 2893 | ||
| 2914 | static void | ||
| 2915 | ns_copy_bits (struct frame *f, NSRect src, NSRect dest) | ||
| 2916 | { | ||
| 2917 | NSTRACE ("ns_copy_bits"); | ||
| 2918 | |||
| 2919 | if (FRAME_NS_VIEW (f)) | ||
| 2920 | { | ||
| 2921 | hide_bell(); // Ensure the bell image isn't scrolled. | ||
| 2922 | |||
| 2923 | ns_focus (f, &dest, 1); | ||
| 2924 | [FRAME_NS_VIEW (f) scrollRect: src | ||
| 2925 | by: NSMakeSize (dest.origin.x - src.origin.x, | ||
| 2926 | dest.origin.y - src.origin.y)]; | ||
| 2927 | ns_unfocus (f); | ||
| 2928 | } | ||
| 2929 | } | ||
| 2930 | 2894 | ||
| 2931 | static void | 2895 | static void |
| 2932 | ns_scroll_run (struct window *w, struct run *run) | 2896 | ns_scroll_run (struct window *w, struct run *run) |
| @@ -2979,8 +2943,12 @@ ns_scroll_run (struct window *w, struct run *run) | |||
| 2979 | { | 2943 | { |
| 2980 | NSRect srcRect = NSMakeRect (x, from_y, width, height); | 2944 | NSRect srcRect = NSMakeRect (x, from_y, width, height); |
| 2981 | NSRect dstRect = NSMakeRect (x, to_y, width, height); | 2945 | NSRect dstRect = NSMakeRect (x, to_y, width, height); |
| 2946 | EmacsView *view = FRAME_NS_VIEW (f); | ||
| 2982 | 2947 | ||
| 2983 | ns_copy_bits (f, srcRect , dstRect); | 2948 | [view copyRect:srcRect to:dstRect]; |
| 2949 | #ifdef NS_IMPL_COCOA | ||
| 2950 | [view setNeedsDisplayInRect:srcRect]; | ||
| 2951 | #endif | ||
| 2984 | } | 2952 | } |
| 2985 | 2953 | ||
| 2986 | unblock_input (); | 2954 | unblock_input (); |
| @@ -3034,20 +3002,12 @@ ns_shift_glyphs_for_insert (struct frame *f, | |||
| 3034 | External (RIF): copy an area horizontally, don't worry about clearing src | 3002 | External (RIF): copy an area horizontally, don't worry about clearing src |
| 3035 | -------------------------------------------------------------------------- */ | 3003 | -------------------------------------------------------------------------- */ |
| 3036 | { | 3004 | { |
| 3037 | //NSRect srcRect = NSMakeRect (x, y, width, height); | 3005 | NSRect srcRect = NSMakeRect (x, y, width, height); |
| 3038 | NSRect dstRect = NSMakeRect (x+shift_by, y, width, height); | 3006 | NSRect dstRect = NSMakeRect (x+shift_by, y, width, height); |
| 3039 | 3007 | ||
| 3040 | NSTRACE ("ns_shift_glyphs_for_insert"); | 3008 | NSTRACE ("ns_shift_glyphs_for_insert"); |
| 3041 | 3009 | ||
| 3042 | /* This doesn't work now as we copy the "bits" before we've had a | 3010 | [FRAME_NS_VIEW (f) copyRect:srcRect to:dstRect]; |
| 3043 | chance to actually draw any changes to the screen. This means in | ||
| 3044 | certain circumstances we end up with copies of the cursor all | ||
| 3045 | over the place. Just mark the area dirty so it is redrawn later. | ||
| 3046 | |||
| 3047 | FIXME: Work out how to do this properly. */ | ||
| 3048 | // ns_copy_bits (f, srcRect, dstRect); | ||
| 3049 | |||
| 3050 | [FRAME_NS_VIEW (f) setNeedsDisplayInRect:dstRect]; | ||
| 3051 | } | 3011 | } |
| 3052 | 3012 | ||
| 3053 | 3013 | ||
| @@ -3167,20 +3127,18 @@ ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row, | |||
| 3167 | 3127 | ||
| 3168 | /* The visible portion of imageRect will always be contained within | 3128 | /* The visible portion of imageRect will always be contained within |
| 3169 | clearRect. */ | 3129 | clearRect. */ |
| 3170 | if (ns_clip_to_rect (f, &clearRect, 1)) | 3130 | ns_focus (f, &clearRect, 1); |
| 3131 | if (! NSIsEmptyRect (clearRect)) | ||
| 3171 | { | 3132 | { |
| 3172 | if (! NSIsEmptyRect (clearRect)) | 3133 | NSTRACE_RECT ("clearRect", clearRect); |
| 3173 | { | ||
| 3174 | NSTRACE_RECT ("clearRect", clearRect); | ||
| 3175 | 3134 | ||
| 3176 | [ns_lookup_indexed_color(face->background, f) set]; | 3135 | [ns_lookup_indexed_color(face->background, f) set]; |
| 3177 | NSRectFill (clearRect); | 3136 | NSRectFill (clearRect); |
| 3178 | } | ||
| 3179 | } | 3137 | } |
| 3180 | 3138 | ||
| 3181 | if (p->which) | 3139 | if (p->which) |
| 3182 | { | 3140 | { |
| 3183 | EmacsImage *img = bimgs[p->which - 1]; | 3141 | EmacsImage *img = bimgs[p->which - 1]; |
| 3184 | 3142 | ||
| 3185 | if (!img) | 3143 | if (!img) |
| 3186 | { | 3144 | { |
| @@ -3213,20 +3171,30 @@ ns_draw_fringe_bitmap (struct window *w, struct glyph_row *row, | |||
| 3213 | [img setXBMColor: bm_color]; | 3171 | [img setXBMColor: bm_color]; |
| 3214 | } | 3172 | } |
| 3215 | 3173 | ||
| 3216 | // Note: For periodic images, the full image height is "h + hd". | 3174 | // Note: For periodic images, the full image height is "h + hd". |
| 3217 | // By using the height h, a suitable part of the image is used. | 3175 | // By using the height h, a suitable part of the image is used. |
| 3218 | NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h); | 3176 | NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h); |
| 3219 | 3177 | ||
| 3220 | NSTRACE_RECT ("fromRect", fromRect); | 3178 | NSTRACE_RECT ("fromRect", fromRect); |
| 3221 | 3179 | ||
| 3222 | [img drawInRect: imageRect | 3180 | /* Because we're drawing into an offscreen buffer which isn't |
| 3223 | fromRect: fromRect | 3181 | flipped, the images come out upside down. To work around it |
| 3224 | operation: NSCompositingOperationSourceOver | 3182 | we need to do some fancy transforms. */ |
| 3225 | fraction: 1.0 | 3183 | { |
| 3226 | respectFlipped: YES | 3184 | NSAffineTransform *transform = [NSAffineTransform transform]; |
| 3227 | hints: nil]; | 3185 | [transform translateXBy:0 yBy:NSMaxY(imageRect)]; |
| 3228 | } | 3186 | [transform scaleXBy:1 yBy:-1]; |
| 3229 | ns_reset_clipping (f); | 3187 | [transform concat]; |
| 3188 | |||
| 3189 | imageRect.origin.y = 0; | ||
| 3190 | } | ||
| 3191 | |||
| 3192 | [img drawInRect: imageRect | ||
| 3193 | fromRect: fromRect | ||
| 3194 | operation: NSCompositingOperationSourceOver | ||
| 3195 | fraction: 1.0 | ||
| 3196 | respectFlipped: YES | ||
| 3197 | hints: nil]; | ||
| 3230 | } | 3198 | } |
| 3231 | ns_unfocus (f); | 3199 | ns_unfocus (f); |
| 3232 | } | 3200 | } |
| @@ -3323,54 +3291,42 @@ ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, | |||
| 3323 | else | 3291 | else |
| 3324 | [FRAME_CURSOR_COLOR (f) set]; | 3292 | [FRAME_CURSOR_COLOR (f) set]; |
| 3325 | 3293 | ||
| 3326 | #ifdef NS_IMPL_COCOA | 3294 | ns_focus (f, &r, 1); |
| 3327 | /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph | ||
| 3328 | atomic. Cleaner ways of doing this should be investigated. | ||
| 3329 | One way would be to set a global variable DRAWING_CURSOR | ||
| 3330 | when making the call to draw_phys..(), don't focus in that | ||
| 3331 | case, then move the ns_unfocus() here after that call. */ | ||
| 3332 | NSDisableScreenUpdates (); | ||
| 3333 | #endif | ||
| 3334 | |||
| 3335 | switch (cursor_type) | ||
| 3336 | { | ||
| 3337 | case DEFAULT_CURSOR: | ||
| 3338 | case NO_CURSOR: | ||
| 3339 | break; | ||
| 3340 | case FILLED_BOX_CURSOR: | ||
| 3341 | NSRectFill (r); | ||
| 3342 | break; | ||
| 3343 | case HOLLOW_BOX_CURSOR: | ||
| 3344 | NSRectFill (r); | ||
| 3345 | [hollow_color set]; | ||
| 3346 | NSRectFill (NSInsetRect (r, 1, 1)); | ||
| 3347 | [FRAME_CURSOR_COLOR (f) set]; | ||
| 3348 | break; | ||
| 3349 | case HBAR_CURSOR: | ||
| 3350 | NSRectFill (r); | ||
| 3351 | break; | ||
| 3352 | case BAR_CURSOR: | ||
| 3353 | s = r; | ||
| 3354 | /* If the character under cursor is R2L, draw the bar cursor | ||
| 3355 | on the right of its glyph, rather than on the left. */ | ||
| 3356 | cursor_glyph = get_phys_cursor_glyph (w); | ||
| 3357 | if ((cursor_glyph->resolved_level & 1) != 0) | ||
| 3358 | s.origin.x += cursor_glyph->pixel_width - s.size.width; | ||
| 3359 | |||
| 3360 | NSRectFill (s); | ||
| 3361 | break; | ||
| 3362 | } | ||
| 3363 | ns_unfocus (f); | ||
| 3364 | 3295 | ||
| 3365 | /* Draw the character under the cursor. Other terms only draw | 3296 | switch (cursor_type) |
| 3366 | the character on top of box cursors, so do the same here. */ | 3297 | { |
| 3367 | if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR) | 3298 | case DEFAULT_CURSOR: |
| 3368 | draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); | 3299 | case NO_CURSOR: |
| 3300 | break; | ||
| 3301 | case FILLED_BOX_CURSOR: | ||
| 3302 | NSRectFill (r); | ||
| 3303 | break; | ||
| 3304 | case HOLLOW_BOX_CURSOR: | ||
| 3305 | NSRectFill (r); | ||
| 3306 | [hollow_color set]; | ||
| 3307 | NSRectFill (NSInsetRect (r, 1, 1)); | ||
| 3308 | [FRAME_CURSOR_COLOR (f) set]; | ||
| 3309 | break; | ||
| 3310 | case HBAR_CURSOR: | ||
| 3311 | NSRectFill (r); | ||
| 3312 | break; | ||
| 3313 | case BAR_CURSOR: | ||
| 3314 | s = r; | ||
| 3315 | /* If the character under cursor is R2L, draw the bar cursor | ||
| 3316 | on the right of its glyph, rather than on the left. */ | ||
| 3317 | cursor_glyph = get_phys_cursor_glyph (w); | ||
| 3318 | if ((cursor_glyph->resolved_level & 1) != 0) | ||
| 3319 | s.origin.x += cursor_glyph->pixel_width - s.size.width; | ||
| 3369 | 3320 | ||
| 3370 | #ifdef NS_IMPL_COCOA | 3321 | NSRectFill (s); |
| 3371 | NSEnableScreenUpdates (); | 3322 | break; |
| 3372 | #endif | 3323 | } |
| 3324 | ns_unfocus (f); | ||
| 3373 | 3325 | ||
| 3326 | /* Draw the character under the cursor. Other terms only draw | ||
| 3327 | the character on top of box cursors, so do the same here. */ | ||
| 3328 | if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR) | ||
| 3329 | draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); | ||
| 3374 | } | 3330 | } |
| 3375 | 3331 | ||
| 3376 | 3332 | ||
| @@ -3455,6 +3411,7 @@ ns_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) | |||
| 3455 | ns_unfocus (f); | 3411 | ns_unfocus (f); |
| 3456 | } | 3412 | } |
| 3457 | 3413 | ||
| 3414 | |||
| 3458 | static void | 3415 | static void |
| 3459 | ns_show_hourglass (struct frame *f) | 3416 | ns_show_hourglass (struct frame *f) |
| 3460 | { | 3417 | { |
| @@ -3978,15 +3935,27 @@ ns_dumpglyphs_image (struct glyph_string *s, NSRect r) | |||
| 3978 | 3935 | ||
| 3979 | [[NSGraphicsContext currentContext] saveGraphicsState]; | 3936 | [[NSGraphicsContext currentContext] saveGraphicsState]; |
| 3980 | 3937 | ||
| 3981 | /* Because of the transforms it's far too difficult to work out | 3938 | /* Because of the transforms it's difficult to work out what |
| 3982 | what portion of the original, untransformed, image will be | 3939 | portion of the original, untransformed, image will be drawn, |
| 3983 | drawn, so the clipping area will ensure we draw only the | 3940 | so the clipping area will ensure we draw only the correct |
| 3984 | correct bit. */ | 3941 | bit. */ |
| 3985 | NSRectClip (dr); | 3942 | NSRectClip (dr); |
| 3986 | 3943 | ||
| 3987 | [setOrigin translateXBy:x - s->slice.x yBy:y - s->slice.y]; | 3944 | [setOrigin translateXBy:x - s->slice.x yBy:y - s->slice.y]; |
| 3988 | [setOrigin concat]; | 3945 | [setOrigin concat]; |
| 3989 | [img->transform concat]; | 3946 | |
| 3947 | NSAffineTransform *doTransform = [NSAffineTransform transform]; | ||
| 3948 | |||
| 3949 | /* We have to flip the image around the X axis as the offscreen | ||
| 3950 | bitmap we're drawing to is flipped. */ | ||
| 3951 | [doTransform scaleXBy:1 yBy:-1]; | ||
| 3952 | [doTransform translateXBy:0 yBy:-[img size].height]; | ||
| 3953 | |||
| 3954 | /* ImageMagick images don't have transforms. */ | ||
| 3955 | if (img->transform) | ||
| 3956 | [doTransform appendTransform:img->transform]; | ||
| 3957 | |||
| 3958 | [doTransform concat]; | ||
| 3990 | 3959 | ||
| 3991 | [img drawInRect:ir fromRect:ir | 3960 | [img drawInRect:ir fromRect:ir |
| 3992 | operation:NSCompositingOperationSourceOver | 3961 | operation:NSCompositingOperationSourceOver |
| @@ -4059,6 +4028,7 @@ static void | |||
| 4059 | ns_dumpglyphs_stretch (struct glyph_string *s) | 4028 | ns_dumpglyphs_stretch (struct glyph_string *s) |
| 4060 | { | 4029 | { |
| 4061 | NSRect r[2]; | 4030 | NSRect r[2]; |
| 4031 | NSRect glyphRect; | ||
| 4062 | int n, i; | 4032 | int n, i; |
| 4063 | struct face *face; | 4033 | struct face *face; |
| 4064 | NSColor *fgCol, *bgCol; | 4034 | NSColor *fgCol, *bgCol; |
| @@ -4066,106 +4036,56 @@ ns_dumpglyphs_stretch (struct glyph_string *s) | |||
| 4066 | if (!s->background_filled_p) | 4036 | if (!s->background_filled_p) |
| 4067 | { | 4037 | { |
| 4068 | n = ns_get_glyph_string_clip_rect (s, r); | 4038 | n = ns_get_glyph_string_clip_rect (s, r); |
| 4069 | |||
| 4070 | ns_focus (s->f, r, n); | 4039 | ns_focus (s->f, r, n); |
| 4071 | 4040 | ||
| 4072 | if (s->hl == DRAW_MOUSE_FACE) | 4041 | if (s->hl == DRAW_MOUSE_FACE) |
| 4073 | { | 4042 | { |
| 4074 | face = FACE_FROM_ID_OR_NULL (s->f, | 4043 | face = FACE_FROM_ID_OR_NULL (s->f, |
| 4075 | MOUSE_HL_INFO (s->f)->mouse_face_face_id); | 4044 | MOUSE_HL_INFO (s->f)->mouse_face_face_id); |
| 4076 | if (!face) | 4045 | if (!face) |
| 4077 | face = FACE_FROM_ID (s->f, MOUSE_FACE_ID); | 4046 | face = FACE_FROM_ID (s->f, MOUSE_FACE_ID); |
| 4078 | } | 4047 | } |
| 4079 | else | 4048 | else |
| 4080 | face = FACE_FROM_ID (s->f, s->first_glyph->face_id); | 4049 | face = FACE_FROM_ID (s->f, s->first_glyph->face_id); |
| 4081 | 4050 | ||
| 4082 | bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f); | 4051 | bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f); |
| 4083 | fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f); | 4052 | fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f); |
| 4084 | 4053 | ||
| 4085 | for (i = 0; i < n; ++i) | 4054 | glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height); |
| 4086 | { | ||
| 4087 | /* FIXME: Why are we reusing the clipping rectangles? The | ||
| 4088 | other terms don't appear to do anything like this. */ | ||
| 4089 | *r = NSMakeRect (s->x, s->y, s->background_width, s->height); | ||
| 4090 | |||
| 4091 | if (s->hl == DRAW_MOUSE_FACE) | ||
| 4092 | { | ||
| 4093 | int overrun, leftoverrun; | ||
| 4094 | |||
| 4095 | /* truncate to avoid overwriting fringe and/or scrollbar */ | ||
| 4096 | overrun = max (0, (s->x + s->background_width) | ||
| 4097 | - (WINDOW_BOX_RIGHT_EDGE_X (s->w) | ||
| 4098 | - WINDOW_RIGHT_FRINGE_WIDTH (s->w))); | ||
| 4099 | r[i].size.width -= overrun; | ||
| 4100 | |||
| 4101 | for (i = 0; i < n; ++i) | ||
| 4102 | { | ||
| 4103 | if (!s->row->full_width_p) | ||
| 4104 | { | ||
| 4105 | int overrun, leftoverrun; | ||
| 4106 | |||
| 4107 | /* truncate to avoid overwriting fringe and/or scrollbar */ | ||
| 4108 | overrun = max (0, (s->x + s->background_width) | ||
| 4109 | - (WINDOW_BOX_RIGHT_EDGE_X (s->w) | ||
| 4110 | - WINDOW_RIGHT_FRINGE_WIDTH (s->w))); | ||
| 4111 | r[i].size.width -= overrun; | ||
| 4112 | |||
| 4113 | /* truncate to avoid overwriting to left of the window box */ | ||
| 4114 | leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w) | ||
| 4115 | + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x; | ||
| 4116 | |||
| 4117 | if (leftoverrun > 0) | ||
| 4118 | { | ||
| 4119 | r[i].origin.x += leftoverrun; | ||
| 4120 | r[i].size.width -= leftoverrun; | ||
| 4121 | } | ||
| 4122 | } | ||
| 4123 | |||
| 4124 | if (leftoverrun > 0) | ||
| 4125 | { | ||
| 4126 | r[i].origin.x += leftoverrun; | ||
| 4127 | r[i].size.width -= leftoverrun; | ||
| 4128 | } | ||
| 4129 | |||
| 4130 | /* XXX: Try to work between problem where a stretch glyph on | ||
| 4131 | a partially-visible bottom row will clear part of the | ||
| 4132 | modeline, and another where list-buffers headers and similar | ||
| 4133 | rows erroneously have visible_height set to 0. Not sure | ||
| 4134 | where this is coming from as other terms seem not to show. */ | ||
| 4135 | r[i].size.height = min (s->height, s->row->visible_height); | ||
| 4136 | } | ||
| 4137 | 4055 | ||
| 4138 | [bgCol set]; | 4056 | [bgCol set]; |
| 4139 | 4057 | ||
| 4140 | /* NOTE: under NS this is NOT used to draw cursors, but we must avoid | 4058 | /* NOTE: under NS this is NOT used to draw cursors, but we must avoid |
| 4141 | overwriting cursor (usually when cursor on a tab) */ | 4059 | overwriting cursor (usually when cursor on a tab) */ |
| 4142 | if (s->hl == DRAW_CURSOR) | 4060 | if (s->hl == DRAW_CURSOR) |
| 4143 | { | 4061 | { |
| 4144 | CGFloat x, width; | 4062 | CGFloat x, width; |
| 4145 | 4063 | ||
| 4146 | x = r[i].origin.x; | 4064 | /* FIXME: This looks like it will only work for left to |
| 4147 | width = s->w->phys_cursor_width; | 4065 | right languages. */ |
| 4148 | r[i].size.width -= width; | 4066 | x = NSMinX (glyphRect); |
| 4149 | r[i].origin.x += width; | 4067 | width = s->w->phys_cursor_width; |
| 4068 | glyphRect.size.width -= width; | ||
| 4069 | glyphRect.origin.x += width; | ||
| 4150 | 4070 | ||
| 4151 | NSRectFill (r[i]); | 4071 | NSRectFill (glyphRect); |
| 4152 | 4072 | ||
| 4153 | /* Draw overlining, etc. on the cursor. */ | 4073 | /* Draw overlining, etc. on the cursor. */ |
| 4154 | if (s->w->phys_cursor_type == FILLED_BOX_CURSOR) | 4074 | if (s->w->phys_cursor_type == FILLED_BOX_CURSOR) |
| 4155 | ns_draw_text_decoration (s, face, bgCol, width, x); | 4075 | ns_draw_text_decoration (s, face, bgCol, width, x); |
| 4156 | else | ||
| 4157 | ns_draw_text_decoration (s, face, fgCol, width, x); | ||
| 4158 | } | ||
| 4159 | else | 4076 | else |
| 4160 | { | 4077 | ns_draw_text_decoration (s, face, fgCol, width, x); |
| 4161 | NSRectFill (r[i]); | ||
| 4162 | } | ||
| 4163 | |||
| 4164 | /* Draw overlining, etc. on the stretch glyph (or the part | ||
| 4165 | of the stretch glyph after the cursor). */ | ||
| 4166 | ns_draw_text_decoration (s, face, fgCol, r[i].size.width, | ||
| 4167 | r[i].origin.x); | ||
| 4168 | } | 4078 | } |
| 4079 | else | ||
| 4080 | { | ||
| 4081 | NSRectFill (glyphRect); | ||
| 4082 | } | ||
| 4083 | |||
| 4084 | /* Draw overlining, etc. on the stretch glyph (or the part | ||
| 4085 | of the stretch glyph after the cursor). */ | ||
| 4086 | ns_draw_text_decoration (s, face, fgCol, NSWidth (glyphRect), | ||
| 4087 | NSMinX (glyphRect)); | ||
| 4088 | |||
| 4169 | ns_unfocus (s->f); | 4089 | ns_unfocus (s->f); |
| 4170 | s->background_filled_p = 1; | 4090 | s->background_filled_p = 1; |
| 4171 | } | 4091 | } |
| @@ -7192,6 +7112,7 @@ not_in_argv (NSString *arg) | |||
| 7192 | from non-native fullscreen, in other circumstances it appears | 7112 | from non-native fullscreen, in other circumstances it appears |
| 7193 | to be a noop. (bug#28872) */ | 7113 | to be a noop. (bug#28872) */ |
| 7194 | wr = NSMakeRect (0, 0, neww, newh); | 7114 | wr = NSMakeRect (0, 0, neww, newh); |
| 7115 | [self createDrawingBufferWithRect:wr]; | ||
| 7195 | [view setFrame: wr]; | 7116 | [view setFrame: wr]; |
| 7196 | 7117 | ||
| 7197 | // To do: consider using [NSNotificationCenter postNotificationName:]. | 7118 | // To do: consider using [NSNotificationCenter postNotificationName:]. |
| @@ -7531,6 +7452,8 @@ not_in_argv (NSString *arg) | |||
| 7531 | maximizing_resize = NO; | 7452 | maximizing_resize = NO; |
| 7532 | #endif | 7453 | #endif |
| 7533 | 7454 | ||
| 7455 | [self createDrawingBufferWithRect:r]; | ||
| 7456 | |||
| 7534 | win = [[EmacsWindow alloc] | 7457 | win = [[EmacsWindow alloc] |
| 7535 | initWithContentRect: r | 7458 | initWithContentRect: r |
| 7536 | styleMask: (FRAME_UNDECORATED (f) | 7459 | styleMask: (FRAME_UNDECORATED (f) |
| @@ -8322,38 +8245,105 @@ not_in_argv (NSString *arg) | |||
| 8322 | } | 8245 | } |
| 8323 | 8246 | ||
| 8324 | 8247 | ||
| 8325 | - (void)viewWillDraw | 8248 | - (void)createDrawingBufferWithRect:(NSRect)rect |
| 8249 | /* Create and store a new NSBitmapImageRep for Emacs to draw | ||
| 8250 | into. | ||
| 8251 | |||
| 8252 | Drawing to an offscreen bitmap doesn't work in GNUstep as there's | ||
| 8253 | a bug in graphicsContextWithBitmapImageRep | ||
| 8254 | (https://savannah.gnu.org/bugs/?38405). So under GNUstep we | ||
| 8255 | retain the old method of drawing direct to the EmacsView. */ | ||
| 8326 | { | 8256 | { |
| 8327 | /* If the frame has been garbaged there's no point in redrawing | 8257 | #ifdef NS_IMPL_COCOA |
| 8328 | anything. */ | 8258 | if (drawingBuffer != nil) |
| 8329 | if (FRAME_GARBAGED_P (emacsframe)) | 8259 | [drawingBuffer release]; |
| 8330 | [self setNeedsDisplay:NO]; | 8260 | |
| 8261 | drawingBuffer = [[self bitmapImageRepForCachingDisplayInRect:rect] retain]; | ||
| 8262 | #endif | ||
| 8331 | } | 8263 | } |
| 8332 | 8264 | ||
| 8333 | - (void)drawRect: (NSRect)rect | 8265 | |
| 8266 | #ifdef NS_IMPL_COCOA | ||
| 8267 | - (void)focusOnDrawingBuffer | ||
| 8334 | { | 8268 | { |
| 8335 | int x = NSMinX (rect), y = NSMinY (rect); | 8269 | /* Creating the graphics context each time is very slow, but it |
| 8336 | int width = NSWidth (rect), height = NSHeight (rect); | 8270 | doesn't seem possible to cache and reuse it. */ |
| 8271 | [NSGraphicsContext | ||
| 8272 | setCurrentContext: | ||
| 8273 | [NSGraphicsContext graphicsContextWithBitmapImageRep:drawingBuffer]]; | ||
| 8274 | } | ||
| 8275 | |||
| 8276 | |||
| 8277 | - (void)windowDidChangeBackingProperties:(NSNotification *)notification | ||
| 8278 | /* Update the drawing buffer when the backing scale factor changes. */ | ||
| 8279 | { | ||
| 8280 | CGFloat old = [[[notification userInfo] | ||
| 8281 | objectForKey:@"NSBackingPropertyOldScaleFactorKey"] | ||
| 8282 | doubleValue]; | ||
| 8283 | CGFloat new = [[self window] backingScaleFactor]; | ||
| 8284 | |||
| 8285 | if (old != new) | ||
| 8286 | { | ||
| 8287 | NSRect frame = [self frame]; | ||
| 8288 | [self createDrawingBufferWithRect:frame]; | ||
| 8289 | ns_clear_frame (emacsframe); | ||
| 8290 | expose_frame (emacsframe, 0, 0, NSWidth (frame), NSHeight (frame)); | ||
| 8291 | } | ||
| 8292 | } | ||
| 8293 | #endif | ||
| 8294 | |||
| 8337 | 8295 | ||
| 8296 | - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect | ||
| 8297 | { | ||
| 8298 | NSTRACE ("[EmacsView copyRect:To:]"); | ||
| 8299 | NSTRACE_RECT ("Source", srcRect); | ||
| 8300 | NSTRACE_RECT ("Destination", dstRect); | ||
| 8301 | |||
| 8302 | #ifdef NS_IMPL_COCOA | ||
| 8303 | [drawingBuffer drawInRect:dstRect | ||
| 8304 | fromRect:srcRect | ||
| 8305 | operation:NSCompositingOperationCopy | ||
| 8306 | fraction:1.0 | ||
| 8307 | respectFlipped:NO | ||
| 8308 | hints:nil]; | ||
| 8309 | |||
| 8310 | [self setNeedsDisplayInRect:dstRect]; | ||
| 8311 | #else | ||
| 8312 | hide_bell(); // Ensure the bell image isn't scrolled. | ||
| 8313 | |||
| 8314 | ns_focus (emacsframe, &dstRect, 1); | ||
| 8315 | [self scrollRect: srcRect | ||
| 8316 | by: NSMakeSize (dstRect.origin.x - srcRect.origin.x, | ||
| 8317 | dstRect.origin.y - srcRect.origin.y)]; | ||
| 8318 | ns_unfocus (emacsframe); | ||
| 8319 | #endif | ||
| 8320 | } | ||
| 8321 | |||
| 8322 | |||
| 8323 | - (void)drawRect: (NSRect)rect | ||
| 8324 | { | ||
| 8338 | NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]", | 8325 | NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]", |
| 8339 | NSTRACE_ARG_RECT(rect)); | 8326 | NSTRACE_ARG_RECT(rect)); |
| 8340 | 8327 | ||
| 8341 | if (!emacsframe || !emacsframe->output_data.ns) | 8328 | if (!emacsframe || !emacsframe->output_data.ns) |
| 8342 | return; | 8329 | return; |
| 8343 | 8330 | ||
| 8331 | #ifdef NS_IMPL_COCOA | ||
| 8332 | [drawingBuffer drawInRect:rect | ||
| 8333 | fromRect:rect | ||
| 8334 | operation:NSCompositingOperationSourceOver | ||
| 8335 | fraction:1 | ||
| 8336 | respectFlipped:NO | ||
| 8337 | hints:nil]; | ||
| 8338 | #else | ||
| 8339 | int x = NSMinX (rect), y = NSMinY (rect); | ||
| 8340 | int width = NSWidth (rect), height = NSHeight (rect); | ||
| 8341 | |||
| 8344 | ns_clear_frame_area (emacsframe, x, y, width, height); | 8342 | ns_clear_frame_area (emacsframe, x, y, width, height); |
| 8345 | block_input (); | 8343 | block_input (); |
| 8346 | expose_frame (emacsframe, x, y, width, height); | 8344 | expose_frame (emacsframe, x, y, width, height); |
| 8347 | unblock_input (); | 8345 | unblock_input (); |
| 8348 | 8346 | #endif | |
| 8349 | /* | ||
| 8350 | drawRect: may be called (at least in Mac OS X 10.5) for invisible | ||
| 8351 | views as well for some reason. Thus, do not infer visibility | ||
| 8352 | here. | ||
| 8353 | |||
| 8354 | emacsframe->async_visible = 1; | ||
| 8355 | emacsframe->async_iconified = 0; | ||
| 8356 | */ | ||
| 8357 | } | 8347 | } |
| 8358 | 8348 | ||
| 8359 | 8349 | ||