Make rackedit tolerant when there's no GUI available
This commit is contained in:
@@ -49,5 +49,16 @@ For example, the window title can be changed before the frame becomes visible:
|
|||||||
The standard File menu does not contain the Exit/Quit command, so closing one
|
The standard File menu does not contain the Exit/Quit command, so closing one
|
||||||
editor window does not imply exiting the application that opened it.
|
editor window does not imply exiting the application that opened it.
|
||||||
|
|
||||||
|
## Headless environments
|
||||||
|
|
||||||
|
Requiring `rackedit` does not initialize Racket's GUI subsystem. This means that
|
||||||
|
`(require rackedit)` is safe in a headless Racket process, for example a server
|
||||||
|
process without access to an X11 or Wayland display.
|
||||||
|
|
||||||
|
The GUI implementation is loaded only when `rkdt` is actually called. Calling
|
||||||
|
`rkdt` still requires a usable graphical environment. Programs that also support
|
||||||
|
a terminal editor can therefore choose that editor before calling `rkdt`, without
|
||||||
|
merely requiring `rackedit` causing GTK initialization.
|
||||||
|
|
||||||
Full API documentation is included as Scribble documentation in
|
Full API documentation is included as Scribble documentation in
|
||||||
`scrbl/rkdt.scrbl`.
|
`scrbl/rkdt.scrbl`.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#lang info
|
#lang info
|
||||||
|
|
||||||
(define pkg-authors '(hnmdijkema))
|
(define pkg-authors '(hnmdijkema))
|
||||||
(define version "0.1.7")
|
(define version "0.1.8")
|
||||||
(define license 'MIT) ;
|
(define license 'MIT) ;
|
||||||
(define collection "rackedit")
|
(define collection "rackedit")
|
||||||
(define pkg-desc "rackedit exports the rkdt procedure, that can be used to open an editor window with a given file")
|
(define pkg-desc "rackedit exports the rkdt procedure, that can be used to open an editor window with a given file")
|
||||||
|
|||||||
@@ -1,101 +1,17 @@
|
|||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
(require framework
|
(require racket/runtime-path)
|
||||||
racket/gui/base
|
|
||||||
racket/class
|
|
||||||
simple-ini/class
|
|
||||||
)
|
|
||||||
|
|
||||||
(provide rkdt)
|
(provide rkdt)
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Internal stuff
|
;; Public interface
|
||||||
|
;;
|
||||||
|
;; Keep the GUI implementation out of this module. This allows packages to
|
||||||
|
;; require rackedit in a headless environment without initializing GTK.
|
||||||
|
;; The GUI is loaded only when rkdt is actually called.
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(define open-editor-counter 0)
|
(define-runtime-path rkdt-gui-path "private/gui.rkt")
|
||||||
(define open-editor-free-list '())
|
|
||||||
(define open-editor-use-list '())
|
|
||||||
|
|
||||||
(define ini (new ini% [file 'rackedit]))
|
|
||||||
|
|
||||||
(define editor-frame%
|
|
||||||
(frame:text-mixin
|
|
||||||
(frame:editor-mixin
|
|
||||||
(frame:standard-menus-mixin
|
|
||||||
(frame:basic-mixin frame%)))))
|
|
||||||
|
|
||||||
(define (make-editor-frame% closed close-cb)
|
|
||||||
(class editor-frame%
|
|
||||||
|
|
||||||
(define my-number -1)
|
|
||||||
(define my-x -1)
|
|
||||||
(define my-y -1)
|
|
||||||
(define my-w -1)
|
|
||||||
(define my-h -1)
|
|
||||||
|
|
||||||
(define/override (on-move x y)
|
|
||||||
(set! my-x x)
|
|
||||||
(set! my-y y)
|
|
||||||
(super on-move x y)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define/override (on-size w h)
|
|
||||||
(set! my-w w)
|
|
||||||
(set! my-h h)
|
|
||||||
(super on-size w h)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define/override (file-menu:create-quit?)
|
|
||||||
#f)
|
|
||||||
|
|
||||||
(define cb (λ args #t))
|
|
||||||
|
|
||||||
(define/public (set-frame-cb! frame-cb)
|
|
||||||
(set! cb frame-cb)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define/augment (on-close)
|
|
||||||
(close-cb my-number my-x my-y my-w my-h)
|
|
||||||
(set! open-editor-use-list
|
|
||||||
(filter (λ (x)
|
|
||||||
(when (= x my-number)
|
|
||||||
(set! open-editor-free-list
|
|
||||||
(sort (cons x open-editor-free-list) <)))
|
|
||||||
(not (= x my-number)))
|
|
||||||
open-editor-use-list))
|
|
||||||
(semaphore-post closed)
|
|
||||||
(cb 'on-close this)
|
|
||||||
(inner (void) on-close))
|
|
||||||
|
|
||||||
(define/public (get-num)
|
|
||||||
my-number)
|
|
||||||
|
|
||||||
(super-new)
|
|
||||||
|
|
||||||
(begin
|
|
||||||
(if (null? open-editor-free-list)
|
|
||||||
(begin
|
|
||||||
(set! open-editor-counter (+ open-editor-counter 1))
|
|
||||||
(set! my-number open-editor-counter))
|
|
||||||
(begin
|
|
||||||
(set! my-number (car open-editor-free-list))
|
|
||||||
(set! open-editor-free-list (cdr open-editor-free-list))))
|
|
||||||
(set! open-editor-use-list (cons my-number open-editor-use-list))
|
|
||||||
)
|
|
||||||
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; Goal: start an editor window for a given file
|
|
||||||
;; pre : *
|
|
||||||
;; post: editor started
|
|
||||||
;; result: An editor window that can be used.
|
|
||||||
;;
|
|
||||||
;; The editor windows are numbered internally and for each editor
|
|
||||||
;; number, a window position x, y, w, h is stored.
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
|
|
||||||
(define (rkdt [filename #f]
|
(define (rkdt [filename #f]
|
||||||
#:wait? [wait? #f]
|
#:wait? [wait? #f]
|
||||||
@@ -105,62 +21,13 @@
|
|||||||
#:y [y 100]
|
#:y [y 100]
|
||||||
#:frame-cb [frame-cb (λ (call-time frame) #t)])
|
#:frame-cb [frame-cb (λ (call-time frame) #t)])
|
||||||
|
|
||||||
(let* ((store-cfg #f)
|
(define gui-rkdt
|
||||||
(closed (make-semaphore 0))
|
(dynamic-require rkdt-gui-path 'rkdt))
|
||||||
(frame (new (make-editor-frame% closed
|
|
||||||
(λ (num x y w h) (store-cfg num x y w h)))
|
|
||||||
[filename (if (eq? filename #f)
|
|
||||||
#f
|
|
||||||
(format "~a" filename))]
|
|
||||||
[editor% racket:text%]
|
|
||||||
[width 900]
|
|
||||||
[height 650]
|
|
||||||
[x 100]
|
|
||||||
[y 100]))
|
|
||||||
(editor (send frame get-editor))
|
|
||||||
(my-num -1)
|
|
||||||
)
|
|
||||||
|
|
||||||
(define (get-win-id num)
|
|
||||||
(letrec ((displ (λ (i n)
|
|
||||||
(if (= i n)
|
|
||||||
""
|
|
||||||
(let-values (((w h) (get-display-size #:monitor i)))
|
|
||||||
(string-append
|
|
||||||
(format "-~a-~a-~a" i w h)
|
|
||||||
(displ (+ i 1) n)))))))
|
|
||||||
(string->symbol
|
|
||||||
(format "win-~a~a" num (displ 0 (get-display-count))))))
|
|
||||||
|
|
||||||
(set! store-cfg (λ (num x y w h)
|
|
||||||
(let ((win (get-win-id num)))
|
|
||||||
(send ini set! 'geoms win (list x y w h)))))
|
|
||||||
|
|
||||||
(send editor set-max-undo-history 100)
|
|
||||||
|
|
||||||
(let* ((win-num (send frame get-num))
|
|
||||||
(geom (send ini get 'geoms
|
|
||||||
(get-win-id win-num)
|
|
||||||
(list x y width height))))
|
|
||||||
|
|
||||||
(send frame move (car geom) (cadr geom))
|
|
||||||
(send frame resize (caddr geom) (cadddr geom))
|
|
||||||
|
|
||||||
(send frame set-label (format "~a (~a)"
|
|
||||||
(send frame get-label)
|
|
||||||
win-num))
|
|
||||||
|
|
||||||
(send frame set-frame-cb! frame-cb)
|
|
||||||
(frame-cb 'before-show frame)
|
|
||||||
(send frame show #t)
|
|
||||||
(frame-cb 'after-show frame)
|
|
||||||
|
|
||||||
(when wait?
|
|
||||||
(frame-cb 'wait-for-close frame)
|
|
||||||
(yield closed)
|
|
||||||
)
|
|
||||||
|
|
||||||
(void))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(gui-rkdt filename
|
||||||
|
#:wait? wait?
|
||||||
|
#:width width
|
||||||
|
#:height height
|
||||||
|
#:x x
|
||||||
|
#:y y
|
||||||
|
#:frame-cb frame-cb))
|
||||||
|
|||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
#lang racket/base
|
||||||
|
|
||||||
|
(require framework
|
||||||
|
racket/gui/base
|
||||||
|
racket/class
|
||||||
|
simple-ini/class
|
||||||
|
)
|
||||||
|
|
||||||
|
(provide rkdt)
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Internal stuff
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(define open-editor-counter 0)
|
||||||
|
(define open-editor-free-list '())
|
||||||
|
(define open-editor-use-list '())
|
||||||
|
|
||||||
|
(define ini (new ini% [file 'rackedit]))
|
||||||
|
|
||||||
|
(define editor-frame%
|
||||||
|
(frame:text-mixin
|
||||||
|
(frame:editor-mixin
|
||||||
|
(frame:standard-menus-mixin
|
||||||
|
(frame:basic-mixin frame%)))))
|
||||||
|
|
||||||
|
(define (make-editor-frame% closed close-cb)
|
||||||
|
(class editor-frame%
|
||||||
|
|
||||||
|
(define my-number -1)
|
||||||
|
(define my-x -1)
|
||||||
|
(define my-y -1)
|
||||||
|
(define my-w -1)
|
||||||
|
(define my-h -1)
|
||||||
|
|
||||||
|
(define/override (on-move x y)
|
||||||
|
(set! my-x x)
|
||||||
|
(set! my-y y)
|
||||||
|
(super on-move x y)
|
||||||
|
)
|
||||||
|
|
||||||
|
(define/override (on-size w h)
|
||||||
|
(set! my-w w)
|
||||||
|
(set! my-h h)
|
||||||
|
(super on-size w h)
|
||||||
|
)
|
||||||
|
|
||||||
|
(define/override (file-menu:create-quit?)
|
||||||
|
#f)
|
||||||
|
|
||||||
|
(define cb (λ args #t))
|
||||||
|
|
||||||
|
(define/public (set-frame-cb! frame-cb)
|
||||||
|
(set! cb frame-cb)
|
||||||
|
)
|
||||||
|
|
||||||
|
(define/augment (on-close)
|
||||||
|
(close-cb my-number my-x my-y my-w my-h)
|
||||||
|
(set! open-editor-use-list
|
||||||
|
(filter (λ (x)
|
||||||
|
(when (= x my-number)
|
||||||
|
(set! open-editor-free-list
|
||||||
|
(sort (cons x open-editor-free-list) <)))
|
||||||
|
(not (= x my-number)))
|
||||||
|
open-editor-use-list))
|
||||||
|
(semaphore-post closed)
|
||||||
|
(cb 'on-close this)
|
||||||
|
(inner (void) on-close))
|
||||||
|
|
||||||
|
(define/public (get-num)
|
||||||
|
my-number)
|
||||||
|
|
||||||
|
(super-new)
|
||||||
|
|
||||||
|
(begin
|
||||||
|
(if (null? open-editor-free-list)
|
||||||
|
(begin
|
||||||
|
(set! open-editor-counter (+ open-editor-counter 1))
|
||||||
|
(set! my-number open-editor-counter))
|
||||||
|
(begin
|
||||||
|
(set! my-number (car open-editor-free-list))
|
||||||
|
(set! open-editor-free-list (cdr open-editor-free-list))))
|
||||||
|
(set! open-editor-use-list (cons my-number open-editor-use-list))
|
||||||
|
)
|
||||||
|
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Goal: start an editor window for a given file
|
||||||
|
;; pre : *
|
||||||
|
;; post: editor started
|
||||||
|
;; result: An editor window that can be used.
|
||||||
|
;;
|
||||||
|
;; The editor windows are numbered internally and for each editor
|
||||||
|
;; number, a window position x, y, w, h is stored.
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|
||||||
|
(define (rkdt [filename #f]
|
||||||
|
#:wait? [wait? #f]
|
||||||
|
#:width [width 900]
|
||||||
|
#:height [height 650]
|
||||||
|
#:x [x 100]
|
||||||
|
#:y [y 100]
|
||||||
|
#:frame-cb [frame-cb (λ (call-time frame) #t)])
|
||||||
|
|
||||||
|
(let* ((store-cfg #f)
|
||||||
|
(closed (make-semaphore 0))
|
||||||
|
(frame (new (make-editor-frame% closed
|
||||||
|
(λ (num x y w h) (store-cfg num x y w h)))
|
||||||
|
[filename (if (eq? filename #f)
|
||||||
|
#f
|
||||||
|
(format "~a" filename))]
|
||||||
|
[editor% racket:text%]
|
||||||
|
[width 900]
|
||||||
|
[height 650]
|
||||||
|
[x 100]
|
||||||
|
[y 100]))
|
||||||
|
(editor (send frame get-editor))
|
||||||
|
(my-num -1)
|
||||||
|
)
|
||||||
|
|
||||||
|
(define (get-win-id num)
|
||||||
|
(letrec ((displ (λ (i n)
|
||||||
|
(if (= i n)
|
||||||
|
""
|
||||||
|
(let-values (((w h) (get-display-size #:monitor i)))
|
||||||
|
(string-append
|
||||||
|
(format "-~a-~a-~a" i w h)
|
||||||
|
(displ (+ i 1) n)))))))
|
||||||
|
(string->symbol
|
||||||
|
(format "win-~a~a" num (displ 0 (get-display-count))))))
|
||||||
|
|
||||||
|
(set! store-cfg (λ (num x y w h)
|
||||||
|
(let ((win (get-win-id num)))
|
||||||
|
(send ini set! 'geoms win (list x y w h)))))
|
||||||
|
|
||||||
|
(send editor set-max-undo-history 100)
|
||||||
|
|
||||||
|
(let* ((win-num (send frame get-num))
|
||||||
|
(geom (send ini get 'geoms
|
||||||
|
(get-win-id win-num)
|
||||||
|
(list x y width height))))
|
||||||
|
|
||||||
|
(send frame move (car geom) (cadr geom))
|
||||||
|
(send frame resize (caddr geom) (cadddr geom))
|
||||||
|
|
||||||
|
(send frame set-label (format "~a (~a)"
|
||||||
|
(send frame get-label)
|
||||||
|
win-num))
|
||||||
|
|
||||||
|
(send frame set-frame-cb! frame-cb)
|
||||||
|
(frame-cb 'before-show frame)
|
||||||
|
(send frame show #t)
|
||||||
|
(frame-cb 'after-show frame)
|
||||||
|
|
||||||
|
(when wait?
|
||||||
|
(frame-cb 'wait-for-close frame)
|
||||||
|
(yield closed)
|
||||||
|
)
|
||||||
|
|
||||||
|
(void))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -64,6 +64,19 @@ The standard File menu does not contain the @onscreen{Exit} or
|
|||||||
@onscreen{Quit} command. Closing an editor window closes only that window.
|
@onscreen{Quit} command. Closing an editor window closes only that window.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section{Headless environments}
|
||||||
|
|
||||||
|
Loading @racketmodname[rackedit] does not initialize Racket's GUI subsystem.
|
||||||
|
The GUI implementation is loaded dynamically only when @racket[rkdt] is
|
||||||
|
actually called. This allows another package to depend on @tt{rackedit} and to
|
||||||
|
be loaded in a headless process without immediately attempting to initialize
|
||||||
|
GTK or connect to a graphical display.
|
||||||
|
|
||||||
|
Calling @racket[rkdt] itself still requires a usable graphical environment. A
|
||||||
|
program that supports both graphical and terminal editors can therefore select
|
||||||
|
a terminal editor before calling @racket[rkdt], while safely keeping
|
||||||
|
@tt{rackedit} as a dependency.
|
||||||
|
|
||||||
@section{Frame callback lifecycle}
|
@section{Frame callback lifecycle}
|
||||||
|
|
||||||
The procedure supplied with @racket[#:frame-cb] is called as
|
The procedure supplied with @racket[#:frame-cb] is called as
|
||||||
|
|||||||
Reference in New Issue
Block a user