aboutsummaryrefslogtreecommitdiff
path: root/witch.ml
blob: 2cb32ccc2a4fbc138c6a3549001236d625bf7548 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
open Sys
open Unix

let try_finally body cleanup =
  let res = try body ()
            with exn -> cleanup (); raise exn in
  cleanup ();
  res

let exn_to_opt body =
  try Some (body ())
  with exn -> None

let exec cmd = execvp cmd.(0) cmd

let launch cmd =
  if fork () = 0
  then exec cmd

let print_usage () =
  List.iter print_endline ["Usage: " ^ Sys.argv.(0) ^ " <title>"];
  exit 0

let die reason =
  print_endline ("ERROR! " ^ reason); exit (-1)

let launch_get_output cmd  =
  let input_lines ic =
    let rec aux lines =
      try aux (input_line ic :: lines) with End_of_file -> lines in
    aux [] in
  let (ifd,ofd) = pipe () in
  match fork () with
  | 0 -> dup2 ofd stdout; dup2 ofd stderr;
         close stdin; close ifd; close ofd;
         exec cmd
  | n -> close ofd;
         let lns = input_lines (in_channel_of_descr ifd) in
         close ifd; ignore (waitpid [] n);
         lns

module Net = struct
  let tcp_server s_handler addr =
    let setup_tcp_socket addr =
      let s = socket PF_INET SOCK_STREAM 0 in
      try bind s addr;
          listen s 10;
          s
      with z -> close s; raise z in
    ignore (signal sigpipe Signal_ignore);
    let ss = setup_tcp_socket addr in
    while true do
      let client = accept ss in
      s_handler ss client
    done

  let double_fork server service (cfd, _ as client) =
    let treat () = match fork () with
      | 0 -> if fork () <> 0 then exit 0;
             close server; service client; exit 0
      | n -> ignore (waitpid [] n) in
    try_finally treat (fun () ->  close cfd)

  let s_handler c_handler sock (_, caddr as client) =
    print_endline
      (match caddr with
       | ADDR_INET (a,_) -> "Connected to " ^ string_of_inet_addr a
       | ADDR_UNIX _     -> "Local connection (dafuq?)");
    double_fork sock c_handler client
end

module type XWIN = sig
    type wid
    val cmdname : string

    val search_by_title : string -> wid list
    val title : wid -> string
    val focus : wid -> unit
    val send : wid -> string -> unit
end
module Xdotool : (XWIN with type wid := string) = struct
  let cmdname = "xdotool"
  let delay = "200"

  let search_by_title name =
    launch_get_output [|cmdname; "search"; "--name"; name|]
  let title wid =
    List.hd (launch_get_output [|cmdname; "getwindowname"; wid|])
  let focus wid =
    launch [|cmdname; "windowfocus"; wid|]
  let send wid keys =
    launch [|cmdname; "type"; "--delay"; delay; keys|]
end

module Comm = struct
  module Window = Xdotool
  module Ms = Map.Make(String)

  let cmds = [("start","m");
              ("up","w"); ("down","s"); ("left","a"); ("right","d");
              ("a","k"); ("b","j");
              ("l","h"); ("r","l")]
  let cmds = List.fold_left (fun m (k,v) -> Ms.add k v m) Ms.empty cmds

  let get_wid name =
    let wids = Window.search_by_title name in
    let falses = [Sys.argv.(0); "mednafen"] in (* May have the same title *)
    let falsewids = List.concat (List.map Window.search_by_title falses) in
    match List.filter (fun wid -> not (List.mem wid falsewids)) wids with
    | [x] -> Some (x, Window.title x)
    | _   -> None

  let c_handler wid (cfd, _) =
    dup2 cfd stdin; dup2 cfd stdout; dup2 cfd stderr; close cfd;
    Window.focus wid;
    while true do
      print_string "> ";
      flush_all ();
      let cmd = read_line () in
      match exn_to_opt (fun () -> Ms.find cmd cmds) with
      | None -> print_endline ("Unknown cmd '" ^ cmd ^ "'")
      | Some key -> print_endline ("Sending cmd '" ^ cmd ^ "' (" ^ key ^ ")...");
                    Window.send wid key
    done

    (* Window.send wid "awdwawdwawdwawdwa" *)
    (* Window.send wid "aaaaaaaaaaaaaaaaa" *)
end

let () =
  if Array.length Sys.argv <> 2 then print_usage ();

  let addr = inet_addr_any in
  let port = 8080 in
  let inaddr = ADDR_INET (addr, port) in
  let name = Sys.argv.(1) in

  match Comm.get_wid name with
  | None              -> die ("Window '" ^ name ^ "' not found")
  | Some (wid, title) ->
     print_endline ("Found window '" ^ title ^ "' (WID " ^ wid ^ ")");
     Net.tcp_server (Net.s_handler (Comm.c_handler wid)) inaddr