From 68d3ea4a6cb9dc3c8081278b33e7e128f8029442 Mon Sep 17 00:00:00 2001 From: Matt Birkholz Date: Wed, 14 Sep 2011 13:27:11 -0700 Subject: [PATCH] Punted ignore-change-region; added buffer-drawing-update-region. Also moved calls to clear-cached-pango-layout to where they cannot cause trouble. Seeing no punted line exposures now! Arranged to integrate unchanged? into the expose event handler. --- src/gtk-screen/gtk-screen.scm | 223 +++++++++++++++++----------------- 1 file changed, 111 insertions(+), 112 deletions(-) diff --git a/src/gtk-screen/gtk-screen.scm b/src/gtk-screen/gtk-screen.scm index 663639725..2fa9c2bd6 100644 --- a/src/gtk-screen/gtk-screen.scm +++ b/src/gtk-screen/gtk-screen.scm @@ -1319,38 +1319,30 @@ USA. ;; allows most expose events to find exposed line-inks ready with a ;; PangoLayout to paint. As lines scroll into view, new PangoLayouts ;; are allocated (or stolen), and the buffer text is re-imaged, -;; styled, and relayed-out just as when originally drawn. Sometimes, +;; styled, and re-layed-out just as when originally drawn. Sometimes, ;; however, the original buffer text is NOT available. ;; -;; When expose events arrive SYNCHRONOUSLY, during the Read part of -;; the editor command loop, the expose event handler can always -;; re-construct a line from the original buffer text. +;; Expose events arrive ASYNCHRONOUSLY, and may find that a line's +;; text has changed. The line may extend into (or just touch!) a +;; buffer's change region. The original buffer text is no longer +;; available, so the original PangoLayout cannot be re-constructed. +;; The event handler must PUNT, and leave the line blank. (It will +;; have been cleared to the background color.) ;; -;; When expose events arrive ASYNCHRONOUSLY, during the Eval or -;; Redisplay parts of the editor command loop, buffers can have -;; non-empty change regions. The event handler may find that the -;; original buffer text is no longer available. It has been modified -;; and thus the original PangoLayout cannot be re-constructed. The -;; event handler must punt, and leave the line blank. (It will have -;; been cleared to the background color.) +;; To ensure that lines can be exposed as soon as they are re-drawn, +;; each buffer drawing keeps an "update region" that it narrows as it +;; redraws. The expose handlers refer to THIS change region, when +;; available (during Redisplay), rather than the buffer's change +;; region. Narrowing a buffer-drawing's update region BEFORE +;; redrawing lines ensures that the resulting expose events will not +;; be punted. ;; -;; These punted exposures should be infrequent. Exposures generated by -;; Scheme's Redisplay process will hopefully be handled synchronously -;; -- batched up until the final gdk_window_process_updates. -;; Exposures by other means are rare. The window manager may -;; restack windows. An application may close a window. Each of -;; these would have to occur during the tiny moment when an editor -;; command is Evaled and the screens Redisplayed. -;; -;; These occasional misses are harmless IF exposures from the -;; Redisplay process are batched up until the final calls to -;; gdk_window_process_updates. Then, with ignore-change-region set, -;; the expose event handlers need not punt. Each changed line will -;; be repainted, including any that had punted an expose event. -;; -;; If this batching cannot be relied upon, some Scheme side batching -;; can be done, and incorrectly exposed regions again queued for -;; redrawing. +;; Punted exposures should be infrequent, resulting from external +;; events (e.g. an obscuring window was closed) exposing lines that +;; have just recently changed in the buffer, during the tiny Eval and +;; Redisplay parts of Edwin's main loop. These occasional misses +;; should be hardly noticeable. The blank line should be quickly +;; redrawn by the end of Redisplay. (define-method update-screen! ((screen ) display-style) (%trace "; (update-screen! ) "screen" "display-style"\n") @@ -1368,27 +1360,21 @@ USA. (else (update-name screen) (update-widgets screen) - (and (begin - (%trace "; update drawings\n") - (for-each-text-widget screen update-widget-buffer) - (if (every (lambda (entry) (update-drawing screen (cdr entry))) - (gtk-screen-drawings screen)) - (begin - (%trace "; update drawings done\n") - #t) - (begin - (%trace "; (update-screen! ) done: halted\n") - #f))) - ;; From here on, drawings are up-to-date, a change region - ;; notwithstanding. - (fluid-let ((ignore-change-region #t)) - (%trace "; update windows\n") - (for-each-text-widget screen update-window) - (if (display-style/discard-screen-contents? display-style) - (for-each-text-widget screen gtk-widget-queue-draw)) - (update-blinking screen) - (%trace "; (update-screen! ) done: finished\n") - #t))))))) + (for-each-text-widget screen update-widget-buffer) + (%trace "; update drawings\n") + (if (every (lambda (entry) (update-drawing screen (cdr entry))) + (gtk-screen-drawings screen)) + (begin + (%trace "; update windows\n") + (for-each-text-widget screen update-window) + (if (display-style/discard-screen-contents? display-style) + (for-each-text-widget screen gtk-widget-queue-draw)) + (update-blinking screen) + (%trace "; (update-screen! ) done: finished\n") + #t) + (begin + (%trace "; (update-screen! ) done: halted\n") + #f))))))) (define-integrable with-screen-in-update (named-lambda (with-screen-in-update screen thunk) @@ -1397,6 +1383,11 @@ USA. (set-screen-in-update?! screen #t) (let ((v (thunk))) (set-screen-in-update?! screen #f) + ;; It would be better if this happened AFTER buffer change + ;; regions were cleared. Or use gdk-window-process-updates here? + (for-each (lambda (buffer.drawing) + (set-buffer-drawing-update-region! (cdr buffer.drawing) #f)) + (gtk-screen-drawings screen)) v))) (define (update-blinking screen) @@ -1686,7 +1677,8 @@ USA. (mark-index display-start))) (change-end-index (if (buffer-drawing-valid? drawing) (group-end-changes-index group) - (mark-index display-end)))) + (mark-index display-end))) + (update-region #f)) (define-syntax %trace3 (syntax-rules () @@ -1706,6 +1698,8 @@ USA. (%trace ";\tno changes\n") #t) (else + (set! update-region (cons change-start-index change-end-index)) + (set-buffer-drawing-update-region! drawing update-region) (let ((finished? (redraw-start @@ -1725,6 +1719,8 @@ USA. lines start num y redraw-end))))))))) + + (set-buffer-drawing-update-region! drawing finished?) (if finished? (begin (set-size) @@ -1957,6 +1953,15 @@ USA. (set! drawing-extent (copy-fix-rect extent)) (fix-rect-union! drawing-extent extent))) + ;; If the update region is narrowed to exclude each line before it + ;; is re-drawn, then the resulting exposes will not be punted by + ;; the line-ink expose handler (which is otherwise shuns change + ;; regions). The expose event could arrive instantly (thread + ;; timer interrupts permitting), so this must be done before + ;; (re)drawing the line-ink. + (define (update-region! start) + (set-car! update-region (mark-index start))) + ;; Keeps the next line to redraw on the front, skipping inks like ;; cursors, selection boxes, embedded images/widgets/whatnot. (define (next-lines inks) @@ -2009,6 +2014,7 @@ USA. (%trace3 "; add-line "start" "num" "y" "old"\n") (let ((new (make-line-ink))) (set-line-ink-start! new (mark-permanent-copy start)) + (update-region! start) (set-line-ink-end! new (mark-permanent-copy start)) (set-line-ink-number! new num) (fix-drawing-add-ink! drawing new (and (pair? old) (car old))) @@ -2019,6 +2025,7 @@ USA. (define (steal-line! line start num y) (%trace3 "; steal-line! "line" "start" "num" "y"\n") (move-mark-to! (line-ink-start line) start) + (update-region! start) (set-line-ink-number! line num) (redraw-line! line 0 y (layout)) (union-ink! line)) @@ -2027,6 +2034,7 @@ USA. (let* ((extent (fix-ink-extent line)) (old-num (line-ink-number line)) (old-y (fix-rect-y extent))) + (update-region! start) (if (not (fix:= old-y y)) (set-text-ink-position! line 0 y)) (if (not (fix:= old-num num)) @@ -2037,10 +2045,10 @@ USA. (next-y-extent extent))) (define (remove-line line) - (clear-cached-pango-layout line) (mark-temporary! (line-ink-start line)) (mark-temporary! (line-ink-end line)) - (fix-ink-remove! line)) + (fix-ink-remove! line) + (clear-cached-pango-layout line)) (define (no-display-changes?) ;; If the drawing already agrees with the buffer and its current @@ -2085,13 +2093,13 @@ USA. (%trace3 ";\t redraw-line! "line" from "(line-ink-start line) " ("x","y") with "pango-layout"\n") - (clear-cached-pango-layout line) (layout-line! line pango-layout) (pango-layout-get-pixel-extents pango-layout (lambda (width height) (without-interrupts (lambda () + (clear-cached-pango-layout line) (%trace3 ";\t erasing "(fix-ink-extent line)"\n") (drawing-damage line) (let ((extent (fix-ink-extent line))) @@ -2131,48 +2139,48 @@ USA. (and (not (group-start-index? group index)) (char=? #\newline (group-left-char group index))))) -(define (unchanged? line) - (let* ((drawing (fix-ink-drawing line)) - (buffer (buffer-drawing-buffer drawing))) - (and buffer - (let* ((group (buffer-group buffer)) - (start-changes-index (group-start-changes-index group))) - (or (not start-changes-index) ;short-circuit no-changes case - (%unchanged? line start-changes-index - (group-end-changes-index group))))))) - -(define (%unchanged? line change-start-index change-end-index) - (or - ;; Common trivial case: no change = unchanged. - (not change-start-index) - - ;; First case: there is a change region, but it ends before - ;; our start. - (let ((start-index (line-ink-start-index line))) - ;; change end = line start is normally considered a miss - ;; (not overlapping) but is incorrect here. A change - ;; abutting the beginning of the line may have removed a - ;; newline... - (and - ;;(fix:< change-end-index start-index) - ;; Is this unnecessary??? - (fix:<= change-end-index start-index) - (fix:< change-start-index start-index) - )) - - ;; Second case: it starts after our end. - (let ((end-index (line-ink-end-index line))) - ;; Now line end = change start IS a miss. A change - ;; abutting the end of the line has only touched its - ;; newline and remains unaffected. YET this is wrong? - ;; - ;; (fix:<= end-index change-start-index) +(define-integrable unchanged? + (named-lambda (unchanged? line) + (let* ((drawing (fix-ink-drawing line)) + (update-region (buffer-drawing-update-region drawing))) + (cond ((eq? update-region #t) #t) + ((pair? update-region) + (or + (let ((change-start (car update-region)) + (line-end (line-ink-end-index line))) + (fix:<= line-end change-start)) + (let ((change-end (cdr update-region)) + (line-start (line-ink-start-index line))) + (fix:< change-end line-start)))) + (else + (let ((buffer (buffer-drawing-buffer drawing))) + (and buffer + (let ((group (buffer-group buffer))) + (%unchanged? line + (group-start-changes-index group) + (group-end-changes-index group)))))))))) + +(define-integrable %unchanged? + (named-lambda (%unchanged? line change-start-index change-end-index) + (or + ;; Common trivial case: no change = unchanged. + (not change-start) + + ;; First case: the change region ends before LINE starts. ;; - ;; If there is NO newline, the line IS affected. A - ;; deletion at the end of the buffer will produce a - ;; change-start at end-of-line/buffer??? + ;; LINE and change region may not touch. The change region may + ;; have removed the newline before LINE, or inserted new text + ;; after the newline, changing LINE's start. + (let ((line-start (line-ink-start-index line))) + (fix:< change-end line-start)) - (fix:< end-index change-start-index)))) + ;; Second case: the change region starts after LINE ends. + ;; + ;; LINE must end with a newline, else a change region touching + ;; the end is adding to the line. Rather than test for this, + ;; consider touching lines as NOT unchanged. + (let ((line-end (line-ink-end-index line))) + (fix:< line-end change-start))))) (define (update-cursor widget) (%trace ";\t update-cursor "widget"\n") @@ -2322,6 +2330,10 @@ USA. (display-start define standard initial-value #f) (display-end define standard initial-value #f) + ;; During redisplay this is the portion of the buffer's change + ;; region that has yet to be re-drawn. + (update-region define standard initial-value #f) + ;; These are the particulars of the set of PangoLayouts in use. ;; Each element is a "cache" containing: (|#f ;; . ). Thus each layout is either idle, or in use -- @@ -2366,24 +2378,11 @@ USA. (write-char #\- port) (write (line-ink-end-index line) port)))) -(define ignore-change-region - ;; fluid-assigned to #t when a buffer drawing is known to be - ;; up-to-date, but its change region has yet to be cleared. - #f) - (define-method text-ink-pango-layout ((ink )) - ;; This procedure is for the expose handler, and mouse tracker, and? - ;; They all seem to be able to fire off ANYTIME. A cached pango - ;; layout is presumed to be all laid out. A cache miss means a - ;; PangoLayout must be re-laid-up from the buffer text, if the text - ;; has not changed. If the change region intersects, the expose - ;; handler must punt (unless ignore-change-region is #t), leaving a - ;; blank spot! A subsequent screen update should damage the punted - ;; line's region. It was intersected by the change region, and will - ;; be updated -- moved/resized/re-texted, or removed entirely. - ;; Presumably this produces only occasional flashes of blank spots - ;; -- an expose sneaking into the tiny Eval-Print parts of the - ;; editor REP loop. + ;; A cached pango layout is presumed to be all laid out. A cache + ;; miss means a PangoLayout must be re-laid-up from the buffer text, + ;; if the text has not changed. If the change region intersects, + ;; punt! (define (salvage-pango-layout line) ;; Look for a cached PangoLayout to re-use. Returns abandoned @@ -2420,14 +2419,14 @@ USA. layout)) ;; Do not (call-next-method ink). There is no method. - (if (or ignore-change-region (unchanged? ink)) + (if (unchanged? ink) (or (line-ink-cached-pango-layout ink) (let ((layout (or (salvage-pango-layout ink) (cache-pango-layout ink)))) (layout-line! ink layout) layout)) (begin - (%trace ";text-ink-pango-layout: punted "ink"\n") + (outf-error ";text-ink-pango-layout: punted "ink"\n") #f))) (define (clear-cached-pango-layout line) -- 2.25.1