Patch title: Release 94 bulk changes
Abstract:
File: /pliant/graphic/browser/naive/client.pli
Key:
    Removed line
    Added line
   
# pliant 'precompile /binary/rip.dump module /pliant/graphic/image/rip.pli' module /pliant/graphic/browser/naive/client.pli command 'browser ""'

module "/pliant/language/compiler.pli"
module "/pliant/language/stream.pli"
module "/pliant/language/stream/memory.pli"
module "core.pli"
module "/pliant/graphic/color/gamut.pli"
module "/pliant/graphic/console/prototype.pli"
module "/pliant/graphic/browser/naive/tag/prototype.pli"
module "/pliant/graphic/browser/naive/tag/all.pli"

module "/pliant/graphic/image/pixmap.pli"
module "/pliant/graphic/image/packed.pli"
module "/pliant/graphic/image/lazy.pli"
module "/pliant/graphic/image/antialiasing.pli"
module "/pliant/graphic/draw/image.pli"
module "/pliant/util/encoding/pack4.pli"
module "/pliant/language/stream/filesystembase.pli"
module "/pliant/protocol/http/chunked.pli"

module "/pliant/language/ui/ansi_terminal.pli"
module "/pliant/fullpliant/user.pli"
module "/pliant/util/crypto/cipher.pli"
module "/pliant/language/type/text/str8.pli"
module "/pliant/admin/md5.pli"


method d delete_recurse n
  arg_rw BrowserDocument d ; arg_rw BrowserNode n
  var Pointer:BrowserNode cur :> n first
  while exists:cur
    var Pointer:BrowserNode next :> cur next
    d delete_recurse cur
    d delete_node cur
    cur :> next

method d reset n
  arg_rw BrowserDocument d ; arg_rw BrowserNode n
  d delete_recurse n
  n reset_attributes

method n count_sons -> nb
  arg BrowserNode n ; arg Int nb
  nb := 0
  each sub n
    nb += 1
    
method n count_nodes -> nb
  arg BrowserNode n ; arg Int nb
  nb := 1
  each sub n
    nb += sub count_nodes
    

method context new_tag tag -> n
  arg_rw BrowserContext context ; arg Str tag ; arg_C BrowserNode n
  implicit context
    n :> document search_node next_id
    if exists:n
      document reset n
      n tag := tag
    else
      n :> document create_node next_id tag
    if previous_id<>""
      var Pointer:BrowserNode p :> document search_node previous_id 
      if exists:p
        document stick n stick_after p
    eif parent_id<>""
      var Pointer:BrowserNode p :> document search_node parent_id 
      if exists:p
        document stick n stick_tail p
    current_id := next_id
    compute_next_id

   
#-------------------------------------------------------------------------------
#  communication protocol


# compact tree instructions

browser_instruction i (var Str id)
  next_id := id

browser_instruction ipush
  stack_id += next_id
  compute_next_id

browser_instruction ipop
  if (exists stack_id:last)
    next_id := stack_id:last
    stack_id remove stack_id:last

browser_instruction t (var Str tag)
  new_tag tag
  parent_id := "" ; previous_id := current_id

browser_instruction a (var Str attr) (var Str value)
  var Pointer:BrowserNode n :> document search_node current_id
  if exists:n
    n attribute attr := value

browser_instruction o (var Str tag)
  new_tag tag
  var BrowserStage s ; s parent_id := "" ; s previous_id := current_id
  stage += s
  parent_id := current_id ; previous_id := ""

browser_instruction c
  var Pointer:BrowserStage s :> stage last
  if exists:s
    parent_id := s parent_id ; previous_id := s previous_id
    stage remove s

browser_instruction push
  var BrowserStage s ; s parent_id := parent_id ; s previous_id := previous_id
  stage += s
  parent_id := "" ; previous_id := ""

browser_instruction pop
  var Pointer:BrowserStage s :> stage last
  if exists:s
    parent_id := s parent_id ; previous_id := s previous_id
    stage remove s

browser_instruction text (var Str txt)
  var Pointer:BrowserNode n :> new_tag "text"
  n attribute "" := txt
  parent_id := "" ; previous_id := current_id


# modify tree instructions

browser_instruction tag (var Str id) (var Str tag)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    document delete_node n
  document create_node id tag

browser_instruction attr (var Str id) (var Str attr) (var Str value)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    n attribute attr := value

browser_instruction stick (var Str id) any:(var Str where)
  var Int mode := stick_before
  if (where parse word:"before" (var Str ref))
    mode := stick_before
  eif (where parse word:"after" (var Str ref))
    mode := stick_after
  eif (where parse word:"head" (var Str ref))
    mode := stick_head
  eif (where parse word:"tail" (var Str ref))
    mode := stick_tail
  else
    mode := undefined
  if mode<>undefined
    var Pointer:BrowserNode n :> document search_node id
    var Pointer:BrowserNode r :> document search_node ref
    if exists:n and exists:r
      document stick n mode r

browser_instruction unstick (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    document unstick n

browser_instruction drop (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    document delete_node n


# styling instruction

browser_instruction style (var Str style_name) (var Str tag) (var Str attribute) (var Str value)
  var Pointer:Dictionary style :> (document:attached first style_name) map Dictionary
  if not exists:style or (entry_type addressof:style)<>Dictionary
    document:attached insert style_name true entry_new:Dictionary
    style :> (document:attached first style_name) map Dictionary
  var Pointer:Str v :> (style first string:style_name+" "+string:tag) map Str
  if not exists:v
    document:attached insert string:style_name+" "+string:tag true entry_new:Str
    v :> (style first string:style_name+" "+string:tag) map Str
  v := value

browser_instruction style_reset (var Str style_name)
  var Pointer:Dictionary style :> (document:attached first style_name) map Dictionary
  document:attached remove style_name null


# windows layout and screen refresh

browser_instruction window_root (var Str name) (var Str id)
  var Pointer:BrowserWindow w :> session window name
  if exists:w
    w root := id
    if not session:scroll_lock
      w scroll_x := 0
      w scroll_y := 0
    w scale := 1
    w refresh := 2

browser_instruction overlay (var Str name) (var Str id)
  var Pointer:BrowserWindow w :> session window name
  if exists:w
    w overlay := id
    w refresh := max w:refresh 1

browser_instruction window_scale (var Str name) (var Float f)
  var Pointer:BrowserWindow w :> session window name
  if exists:w
    if f>=1e-3 and f<=1e3
      w scroll_x := cast w:scroll_x*f/w:scale Int
      w scroll_y := cast w:scroll_y*f/w:scale Int
      w scale := f
      w refresh := 2

browser_instruction window_scroll (var Str name) (var Float x0) (var Float y0) (var Float x1) (var Float y1)
  var Pointer:BrowserWindow w :> session window name
  if exists:w
    var Int ix0 := cast x0/console:unit_x*w:scale-0.5 Int
    var Int iy0 := cast y0/console:unit_y*w:scale-0.5 Int
    var Int ix1 := cast x1/console:unit_x*w:scale+0.5 Int
    var Int iy1 := cast y1/console:unit_y*w:scale+0.5 Int
    w scroll (min (max w:scroll_x ix1-(w:x1-w:x0)) ix0) (min (max w:scroll_y iy1-(w:y1-w:y0)) iy0)
  

browser_instruction node_draw (var Str id)
  session node_draw id


browser_instruction node_refresh (var Str id) (var Float mm_x) (var Float mm_y) any:(var Str options)
  session node_refresh id mm_x mm_y options


browser_instruction window_refresh (var Str name) (var Int level)
  var Pointer:BrowserWindow w :> session window name
  if exists:w and level>=0 and level<=2
    w refresh := level
  
browser_instruction window_refresh2 (var Str id) (var Int level)
  var Pointer:BrowserNode n :> session:document search_node id
  var Pointer:BrowserWindow w :> session window n
  if exists:w and level>=0 and level<=2
    w refresh := level


browser_instruction next_id (var Str h) (var uInt c)
  session next_id_header := h
  session next_id_counter := c
  compute_next_id


browser_instruction display
  session display

browser_instruction window_border (var Str name) any
  var Pointer:BrowserWindow w :> session window name
  if exists:w
    w border_size := line option "size" Int 1
    w border_color := console browser_color (line option "color" Str "FFFFFF")

browser_instruction window_antialiasing (var Str name) (var Int i)
  var Pointer:BrowserWindow w :> session window name
  if exists:w
    w antialiasing := shunt i=1 or i=2 or i=4 or i=8 i 1

browser_instruction layout_space any
  session space_size := line option "size" Int 1
  session space_color := console browser_color (line option "color" Str "000000")


browser_instruction focus_set (var Str hook) (var Str id) (var Int index) (var Str options)
  session focus_set (shunt hook<>"" hook current_id) (shunt id<>"" id current_id) index options

browser_instruction focus_get
  connection writeline (string session:focus_hook)+" "+(string session:focus_id)+" "+(string session:focus_index)+" "+(string session:focus_options)

browser_instruction focus_save
  var BrowserFocusHistory h
  h hook := session focus_hook
  h id := session focus_id
  h index := session focus_index
  h options := session focus_options
  session focus_history += h

browser_instruction key (var Str key) (var Str id)
  session:keys insert key (shunt id<>"" id current_id)

browser_instruction key_cleanup
  var Pointer:Str k :> session:keys first
  while exists:k
    var Pointer:Str n :> session:keys next k
    if not exists:(session:document search_node k)
      session:keys remove k
    k :> n

browser_instruction ack (var Str ack)
  if session:event_ack=ack
    session event_ack := ""


# urls

browser_instruction url_set (var Str path) (var Str options)
  session path := path
  session options := options

browser_instruction url_call (var Str path) (var Str options)
  session history_push
  session connect path options

browser_instruction url_return
  session history_pull
  session connect session:path session:options


# bitmaps

browser_instruction image (var Str id) (var Float x0) (var Float y0) (var Float x1) (var Float y1) (var Int size_x) (var Int size_y) (var Str gamutname) any:(var Str extra)
  var Link:ColorGamut gamut :> color_gamut gamutname
  if gamut=success
    var Link:ImagePacked img :> new ImagePacked
    img setup (image_prototype x0 y0 x1 y1 size_x size_y gamut) extra
    document:attached insert id true addressof:img
  else
    syntax_error "Unknown gamut '"+gamutname+"'"

browser_instruction paint (var Str id) (var Int x0) (var Int y0) (var Int x1) (var Int y1)
  var Link:ImagePacked img :> (document:attached first id) map ImagePacked
  if exists:img and (entry_type addressof:img)=ImagePacked
    if x0>=0 and y0>=0 and x1>x0 and y1>y0 and x1<=img:size_x and y1<=img:size_y
      var Address previous := memory_zallocate (x1-x0)*img:pixel_size null
      var Address cbuffer := memory_allocate (x1-x0)*img:pixel_size*2+Int:size null 
      var Address buffer := memory_allocate (x1-x0)*img:pixel_size null
      for (var Int iy) y0 y1-1
        if (connection:readline parse word:"b" (var Int csize)) and csize>=0 and csize<=memory_size:cbuffer
          connection raw_read cbuffer csize
          pack4_decode cbuffer buffer img:pixel_size x1-x0 previous
          img write x0 iy x1-x0 buffer
          swap buffer previous
    else
      syntax_error "Bounds overflow in '"+id+"' image"
  else
    syntax_error "There is no '"+id+"' image"

browser_instruction overpaint (var Str id) (var Int x0) (var Int y0) (var Int x1) (var Int y1)
  var Link:ImagePacked img :> (document:attached first id) map ImagePacked
  if exists:img and (entry_type addressof:img)=ImagePacked
    if x0>=0 and y0>=0 and x1>x0 and y1>y0 and x1<=img:size_x and y1<=img:size_y
      var Address previous := memory_allocate (x1-x0)*img:pixel_size null
      var Address cbuffer := memory_allocate (x1-x0)*img:pixel_size*2+Int:size null 
      var Address buffer := memory_allocate (x1-x0)*img:pixel_size null
      for (var Int iy) y0 y1-1
        if (connection:readline parse word:"b" (var Int csize)) and csize>=0 and csize<=memory_size:cbuffer
          connection raw_read cbuffer csize
          img read x0 iy x1-x0 previous
          pack4_decode cbuffer buffer img:pixel_size x1-x0 previous
          img write x0 iy x1-x0 buffer
    else
      syntax_error "Bounds overflow in '"+id+"' image"
  else
    syntax_error "There is no '"+id+"' image"

browser_instruction jpeg (var Str id) (var Int x0) (var Int y0) (var Int x1) (var Int y1)
  var Link:ImagePacked img :> (document:attached first id) map ImagePacked
  if exists:img and (entry_type addressof:img)=ImagePacked
    var Link:Stream s :> new Stream
    s open "chunked:" "pliant_browser_chunking" in+safe pliant_default_file_system connection
    var Link:ImageLazy lazy :> new ImageLazy
    lazy bind s "filter [dq].jpeg[dq]"
    if x0>=0 and y0>=0 and x1>x0 and y1>y0 and x1<=img:size_x and y1<=img:size_y and x1-x0=lazy:size_x and y1-y0=lazy:size_y and img:pixel_size=lazy:pixel_size
      var Address buffer := memory_allocate (x1-x0)*img:pixel_size null
      for (var Int iy) y0 y1-1
        lazy read 0 iy-y0 x1-x0 buffer
        img write x0 iy x1-x0 buffer
    else
      syntax_error "Bounds overflow in '"+id+"' image"
  else
    syntax_error "There is no '"+id+"' image"

browser_instruction copy (var Str sid) (var Int sx0) (var Int sy0) (var Int sx1) (var Int sy1) (var Str did) (var Int dx0) (var Int dy0)
  var Link:ImagePacked src :> (document:attached first sid) map ImagePacked
  var Link:ImagePacked dest :> (document:attached first did) map ImagePacked
  if exists:src and (entry_type addressof:src)=ImagePacked and exists:dest and (entry_type addressof:dest)=ImagePacked
    if sx0>=0 and sy0>=0 and sx1>sx0 and sy1>sy0 and sx1<=src:size_x and sy1<=src:size_y and dx0>=0 and dy0>=0 and dx0+(sx1-sx0)<=dest:size_x and dy0+(sy1-sy0)<=dest:size_y and src:pixel_size=dest:pixel_size
      var Address buffer := memory_allocate (sx1-sx0)*src:pixel_size null
      if dy0<=sy0
        for (var Int iy) sy0 sy1-1
          src read sx0 iy sx1-sx0 buffer
          dest write dx0 dy0+(iy-sy0) sx1-sx0 buffer
      else
        for (var Int iy) sy1-1 sy0 step -1
          src read sx0 iy sx1-sx0 buffer
          dest write dx0 dy0+(iy-sy0) sx1-sx0 buffer
    else
      syntax_error "Bounds overflow in '"+sid+"' or '"+did+"' image"
    void
  else
    syntax_error "There is no '"+sid+"' or '"+did+"' image"


# server queries about positions

browser_instruction query_node_position (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n and n:has_area
    connection writeline (string n:area:x0)+" "+(string n:area:y0)+" "+(string n:area:x1)+" "+(string n:area:y1)
  else
    connection writeline ""

browser_instruction query_window_bbox (var Str name)
  var Pointer:BrowserWindow w :> session window name
  if exists:w
    connection writeline (string w:bbox:x0)+" "+(string w:bbox:y0)+" "+(string w:bbox:x1)+" "+(string w:bbox:y1)
  else
    connection writeline ""

browser_instruction query_window_scroll (var Str name)
  var Pointer:BrowserWindow w :> session window name
  if exists:w and w:x0<>undefined
    connection writeline (string w:scroll_x*console:unit_x/w:scale)+" "+(string w:scroll_y*console:unit_y/w:scale)+" "+(string (w:scroll_x+w:x1-w:x0)*console:unit_x/w:scale)+" "+(string (w:scroll_y+w:y1-w:y0)*console:unit_y/w:scale)
  else
    connection writeline ""

browser_instruction query_window_scale (var Str name)
  var Pointer:BrowserWindow w :> session window name
  if exists:w and w:x0<>undefined
    connection writeline (string w:scale)
  else
    connection writeline ""

browser_instruction query_screen
  connection writeline (string console:size_x*console:unit_x)+" "+(string console:size_y*console:unit_y)+" "+(string console:size_x)+" "+(string console:size_y)


# server queries about content

browser_instruction path (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  while exists:n
    connection writeline "i "+(string n:id)
    connection writeline "t "+(string n:tag)
    var CBool found := n first_attribute (var Str a) (var Str v)
    while found
      connection writeline "a "+string:a+" "+string:v
      found := n next_attribute a v
    n :> n parent
  connection writeline ""

method n send_tree_recurse stream
  arg BrowserNode n ; arg_rw Stream stream
  stream writeline "i "+(string n:id)
  stream writeline (shunt (exists n:first) "o " "l ")+(string n:tag)
  var CBool found := n first_attribute (var Str a) (var Str v)
  while found
    stream writeline "a "+string:a+" "+string:v
    found := n next_attribute a v
  if (exists n:first)
    each sub n
      sub send_tree_recurse stream
    stream writeline "c"
    
browser_instruction tree (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    n send_tree_recurse connection
  connection writeline ""

browser_instruction small_tree (var Str id) (var Int limit)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n and n:count_nodes<=limit
    connection writeline "yes"
    n send_tree_recurse connection
    connection writeline ""
  else
    connection writeline "no"

browser_instruction sons (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    each sub n
      connection writeline "i "+(string sub:id)
      connection writeline "l "+(string sub:tag)
      var CBool found := sub first_attribute (var Str a) (var Str v)
      while found
        connection writeline "a "+string:a+" "+string:v
        found := sub next_attribute a v
  connection writeline ""

browser_instruction count_tree (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    connection writeline (string n:count_nodes)
  else
    connection writeline "0"

browser_instruction count_sons (var Str id)
  var Pointer:BrowserNode n :> document search_node id
  if exists:n
    var Int count := 0
    each sub n
      count += 1
    connection writeline string:count
  else
    connection writeline ""


# misc instructions

browser_instruction status
  connection writeline (shunt status=success "success" "failure "+(string status:message))
  status := success

browser_instruction thread (var Str id)
  var Link:Stream stream :> new Stream
  session:extra_connections += stream
  thread
    console connect session:path stream
    stream writeline "thread "+string:id
    console:sem request
    var BrowserContext c2
    c2 bind session stream
    # c2 process_instructions FIXME: restore it
    var (Pointer Link:Stream) p :>> session:extra_connections first
    while exists:p and addressof:p<>addressof:stream
      p :>> session:extra_connections next p
    if addressof:p=addressof:stream
      session:extra_connections remove p
    console:sem release

if false

  browser_instruction multi # reduce latency
    var List:Str lines
    while { var Str l2 := connection readline ; l2<>"" }
      lines += l2
    each l lines
      var Pointer:Function fun :> (browser_instructions first (l 0 (l search " " l:len))) map Function
      if exists:fun
        process_browser_instruction l context fun
      else
        context syntax_error "unknown '"+(l 0 (l search " " l:len))+"' instruction"


#-------------------------------------------------------------------------------
#  main loop


function browser options -> status
  arg Str options ; arg ExtendedStatus status
  var BrowserConsole c
  c:sem request
  var Int dpi := options option "dpi" Int 100
  c unit_x := 25.4/dpi ; c unit_y := 25.4/dpi
  var Int aa := options option "antialiasing" Int 1
  for (var Int i) 0 c:session:size-1
    each w c:session:i:windows
      w antialiasing := aa
  c console :> graphic_console (options option "console" Str "x11")
  if not (exists c:console)
    return (failure "There is no '"+(options option "console" Str "x11")+"' console driver.")
  var Int size_x := cast 480*dpi/75 Int ; var Int size_y := cast 600*dpi/75 Int
  var ImagePrototype proto := image_prototype 0 0 size_x*c:unit_x size_y*c:unit_y size_x size_y color_gamut:"bgr"
  c:console open proto options
  c rgb :> color_gamut "bgr"
  proto := c:console query ; c size_x := proto size_x ; c size_y := proto size_y ; c gamut :> proto gamut
  var Str user := options option "user" Str
  if user<>""
    var CBool secured := options option "secured"
    var Str pass := keyboard_input_password "Secured password for "+user+": "
    if secured
      var CBool ok := user_secret_database:data:user:user:key_md5="" or user_secret_database:data:user:user:key_md5=string_md5_hexa_signature:(uncipher (straight_to_Str user_secret_database:data:user:user:private_key) pass)
      if not ok
        console "Wrong password." eol
        return
    for (var Int i) 0 11
      c:session:i user := user
      c:session:i password := pass
      c:session:i secured := secured
  if (c:session:11 connect (options option "supervisor" Str "pliant://localhost/supervisor/") "")=failure
    console "failed to connnect 1" eol
    return
  if (c:session:0 connect (options option "url" Str "pliant://localhost/") "")=failure
    console "failed to connnect 2" eol
    return
  var Int last_x last_y
  while true
    c:sem release
    var Str event := c:console event (var Str key) (var Int buttons) (var Int x0) (var Int y0) (var Int x1) (var Int y1) (var Str ev_options)
    c:sem request
    if false
      console "event " event " key " key " buttons " buttons " at " x0 " " y0 " " x1 " " y1 eol
    if event="press"
      last_x := x0 ; last_y := y0
    if event="move"
      if buttons<>undefined and (buttons .and. 4)<>0
        for (var Int i) 0 11
          var Pointer:BrowserSession s :> c:session i
          each w s:windows
            if w:x0<>undefined and x0>=w:x0 and y0>=w:y0 and x0<w:x1 and y0<w:y1
              var Int speed_x := max (min (cast (w:bbox:x1-w:bbox:x0)/c:unit_x/c:size_x Int) c:size_x\8) 1
              var Int speed_y := max (min (cast (w:bbox:y1-w:bbox:y0)/c:unit_y/c:size_y Int) c:size_y\8) 1
              w scroll w:scroll_x+(last_x-x0)*speed_x w:scroll_y+(last_y-y0)*speed_y
      last_x := x0 ; last_y := y0
    eif event="redraw"
      c draw x0 y0 x1 y1 true
    eif event="resize"
      c size_x := x0 ; c size_y := y0
      c refresh
    eif event="press" and (key eparse "shift F" (var Int i)) and i>=1 and i<12 and c:zorder:0<>11
      c raise i-1 true
      (c:session c:zorder:0) display
    eif event="press" and (key eparse "ctrl F" (var Int i)) and i>=1 and i<=12
      c raise i-1 false
      (c:session c:zorder:0) display
    eif event="press" and (key parse word:"ctrl" any:(var Str k2)) and k2:len=1 and { var Int i := "azeqsdwxc" search k2 -1 ; i<>(-1) }
      var Pointer:BrowserSession s :> c:session c:zorder:0 
      s position := i
      s display
    eif event="press" and (key parse word:"ctrl" any:(var Str k2)) and k2<>"" and ("left up right down" search k2 -1)<>(-1)
      var Float step := 0.05
      if k2="left"
        c middle_x := max c:middle_x-step step
      eif k2="up"
        c middle_y := max c:middle_y-step step
      eif k2="right"
        c middle_x := min c:middle_x+step 1-step
      eif k2="down"
        c middle_y := min c:middle_y+step 1-step
      s display
    eif event="press" and key="ctrl escape"
      for (var Int i) 0 c:session:size-1
        each w c:session:i:windows
          w memo := w antialiasing ; w antialiasing := max w:antialiasing 4
      c refresh
      for (var Int i) 0 c:session:size-1
        each w c:session:i:windows
          w antialiasing := w memo
    else
      if event="press" and (key parse "button" (var Int i))
        for (var Int i) 0 11
          var Pointer:BrowserSession s :> c:session i
          var BrowserContext context
          context bind s s:connection
          each w s:windows
            if w:x0<>undefined and x0>=w:x0 and y0>=w:y0 and x0<w:x1 and y0<w:y1
              c raise i false
      var Pointer:BrowserSession s :> c:session c:zorder:0
      var BrowserEvent ev
      ev id := generate_id
      ev event := event ; ev key := key ; ev buttons := buttons ; ev x := undefined ; ev y := undefined ; ev window :> null map BrowserWindow
      each w s:windows
        if w:x0<>undefined and x0>=w:x0 and y0>=w:y0 and x0<w:x1 and y0<w:y1
          ev x := (x0-w:x0+w:scroll_x+0.5)*s:console:unit_x/w:scale
          ev y := (y0-w:y0+w:scroll_y+0.5)*s:console:unit_y/w:scale
          ev window :> w
      s event_list += ev
      s process_events
  status := success

export browser