! :folding=indent:collapseFolds=1: ! $Id$ ! ! Copyright (C) 2004, 2005 Slava Pestov. ! ! Redistribution and use in source and binary forms, with or without ! modification, are permitted provided that the following conditions are met: ! ! 1. Redistributions of source code must retain the above copyright notice, ! this list of conditions and the following disclaimer. ! ! 2. Redistributions in binary form must reproduce the above copyright notice, ! this list of conditions and the following disclaimer in the documentation ! and/or other materials provided with the distribution. ! ! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, ! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND ! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ! A graphical console. ! ! To run this code, bootstrap Factor like so: ! ! ./f boot.image.le32 ! -libraries:sdl:name=libSDL.so ! -libraries:sdl-gfx:name=libSDL_gfx. ! ! (But all on one line) ! ! Then, start Factor as usual (./f factor.image) and enter this ! at the listener: ! ! USE: shells ! sdl IN: console USE: generic USE: vectors USE: sdl USE: sdl-event USE: sdl-gfx USE: sdl-video USE: namespaces USE: math USE: kernel USE: strings USE: alien USE: sdl-keyboard USE: streams USE: prettyprint USE: listener USE: threads USE: stdio USE: errors USE: line-editor USE: hashtables USE: lists USE: sdl-ttf #! A namespace holding console state. SYMBOL: console #! A vector. New lines are pushed on the end. SYMBOL: lines #! An integer. Line at top of screen. SYMBOL: first-line #! Current X co-ordinate. SYMBOL: x #! Current Y co-ordinate. SYMBOL: y #! A string buffer. SYMBOL: output-line #! A line editor object. SYMBOL: input-line #! A TTF_Font* value. SYMBOL: console-font #! Font height. SYMBOL: line-height #! If this is on, the console will be redrawn on the next event #! refresh cycle. SYMBOL: redraw-console #! The font size is hardcoded here. : char-width 8 ; ! Scrolling : visible-lines ( -- n ) height get line-height get /i ; : total-lines ( -- n ) lines get vector-length ; : available-lines ( -- ) total-lines first-line get - ; : fix-first-line ( line -- line ) total-lines visible-lines - 1 + min 0 max ; : change-first-line ( quot -- ) first-line get swap call fix-first-line first-line set ; inline : line-scroll-up ( -- ) [ 1 - ] change-first-line ; : line-scroll-down ( -- ) [ 1 + ] change-first-line ; : page-scroll-up ( -- ) [ visible-lines - ] change-first-line ; : page-scroll-down ( -- ) [ visible-lines + ] change-first-line ; : scroll-to-bottom ( -- ) total-lines fix-first-line first-line set ; ! Rendering : background white ; : foreground black ; : cursor red ; : next-line ( -- ) 0 x set line-height get y [ + ] change ; : draw-line ( str -- ) >r x get y get console-font get r> foreground make-color background make-color draw-string x [ + ] change ; : clear-display ( -- ) surface get 0 0 width get height get background rgb boxColor ; : draw-lines ( -- ) visible-lines available-lines min [ dup first-line get + lines get vector-nth draw-line next-line ] repeat ; : blink-interval 500 ; : draw-cursor ( x -- ) surface get swap y get over 1 + y get line-height get + cursor rgb boxColor ; : draw-current ( -- ) output-line get sbuf>str draw-line ; : caret-x ( -- x ) x get input-line get [ console-font get caret get line-text get str-head size-string drop + ] bind ; : draw-input ( -- ) caret-x >r input-line get [ line-text get ] bind draw-line r> draw-cursor ; : scrollbar-width 16 ; : scroll-y ( line -- y ) total-lines 1 + / height get * ; : scrollbar-top ( -- y ) first-line get scroll-y ; : scrollbar-bottom ( -- y ) first-line get visible-lines + scroll-y ; : draw-scrollbar ( -- ) surface get width get scrollbar-width - scrollbar-top width get scrollbar-bottom black rgb boxColor ; : draw-console ( -- ) [ 0 x set 0 y set clear-display draw-lines height get y get - line-height get >= [ draw-current draw-input ] when draw-scrollbar ] with-surface ; : empty-buffer ( sbuf -- str ) dup sbuf>str 0 rot set-sbuf-length ; : add-line ( text -- ) lines get vector-push scroll-to-bottom ; : console-write ( text -- ) "\n" split1 [ swap output-line get sbuf-append output-line get empty-buffer add-line ] when* output-line get sbuf-append ; ! The console stream ! Restoring this continuation with a string on the stack returns ! to the caller of freadln. SYMBOL: input-continuation TUPLE: console-stream console redraw-continuation ; C: console-stream ( console console-continuation -- stream ) [ set-console-stream-redraw-continuation ] keep [ set-console-stream-console ] keep ; M: console-stream fflush ( stream -- ) fauto-flush ; M: console-stream fauto-flush ( stream -- ) console-stream-console [ redraw-console on ] bind ; M: console-stream freadln ( stream -- line ) [ swap [ console-stream-console [ input-continuation set ] bind ] keep dup console-stream-redraw-continuation dup [ call ] [ drop f ] ifte ] callcc1 nip ; M: console-stream fwrite-attr ( string style stream -- ) nip console-stream-console [ console-write ] bind ; M: console-stream fclose ( stream -- ) drop ; ! Event handling SYMBOL: event : valid-char? 1 255 between? ; : return-key input-line get [ commit-history line-text get line-clear ] bind dup console-write "\n" console-write input-continuation get call ; GENERIC: handle-event ( event -- ? ) SYMBOL: keymap {{ [[ [ "RETURN" ] [ return-key ] ]] [[ [ "BACKSPACE" ] [ input-line get [ backspace ] bind ] ]] [[ [ "LEFT" ] [ input-line get [ left ] bind ] ]] [[ [ "RIGHT" ] [ input-line get [ right ] bind ] ]] [[ [ "UP" ] [ input-line get [ history-prev ] bind ] ]] [[ [ "SHIFT" "DOWN" ] [ line-scroll-down ] ]] [[ [ "SHIFT" "UP" ] [ line-scroll-up ] ]] [[ [ "PAGEDOWN" ] [ page-scroll-down ] ]] [[ [ "PAGEUP" ] [ page-scroll-up ] ]] [[ [ "DOWN" ] [ input-line get [ history-next ] bind ] ]] [[ [ "CTRL" "k" ] [ input-line get [ line-clear ] bind ] ]] }} keymap set : input-key? ( event -- ? ) #! Is this a keystroke that potentially inserts input, or #! does it have modifiers? keyboard-event-unicode valid-char? ; : user-input ( char -- ) input-line get [ insert-char ] bind scroll-to-bottom ; M: key-down-event handle-event ( event -- ? ) dup keyboard-event>binding keymap get hash [ call redraw-console on ] [ dup input-key? [ keyboard-event-unicode user-input redraw-console on ] [ drop ] ifte ] ?ifte t ; ! The y co-ordinate of the start of the drag. SYMBOL: drag-start-y ! The first line at the time SYMBOL: drag-start-line : scrollbar-click ( y -- ) dup scrollbar-top < [ drop page-scroll-up redraw-console on ] [ dup scrollbar-bottom > [ drop page-scroll-down redraw-console on ] [ drag-start-y set first-line get drag-start-line set ] ifte ] ifte ; M: button-down-event handle-event ( event -- ? ) dup button-event-x width get scrollbar-width - >= [ button-event-y scrollbar-click ] [ drop ] ifte t ; M: button-up-event handle-event ( event -- ? ) drop drag-start-y off drag-start-line off t ; M: motion-event handle-event ( event -- ? ) drag-start-y get [ motion-event-y drag-start-y get - height get / total-lines * drag-start-line get + >fixnum fix-first-line first-line set redraw-console on ] [ drop ] ifte t ; M: resize-event handle-event ( event -- ? ) dup resize-event-w swap resize-event-h 0 SDL_HWSURFACE SDL_RESIZABLE bitor init-screen scroll-to-bottom redraw-console on t ; M: quit-event handle-event ( event -- ? ) drop f ; M: alien handle-event ( event -- ? ) drop t ; : check-event ( -- ? ) #! Check if there is a pending event. #! Return if we should continue or stop. event get dup SDL_PollEvent [ handle-event [ check-event ] [ f ] ifte ] [ drop t ] ifte ; : set-console-font ( font ptsize ) font dup console-font set TTF_FontHeight line-height set ; : init-console ( -- ) TTF_Init "/fonts/VeraMono.ttf" 14 set-console-font event set 0 first-line set 80 lines set input-line set 80 output-line set 1 SDL_EnableUNICODE drop SDL_DEFAULT_REPEAT_DELAY SDL_DEFAULT_REPEAT_INTERVAL SDL_EnableKeyRepeat drop ; : console-loop ( -- ) redraw-console get [ draw-console redraw-console off ] when check-event [ console-loop ] when ; : console-quit ( -- ) input-continuation get [ f swap call ] when* SDL_Quit ; SYMBOL: escape-continuation IN: shells : sdl ( -- ) [ 640 480 0 SDL_HWSURFACE SDL_RESIZABLE bitor init-screen init-console ] extend console set [ escape-continuation set [ console get swap [ print-banner listener ] with-stream SDL_Quit ( return from start-console word ) escape-continuation get call ] callcc0 console get [ redraw-console on console-loop console-quit ] bind ] callcc0 ;