Simulate input/Keyboard

From Rosetta Code
Revision as of 02:58, 11 May 2010 by MikeMol (talk | contribs) (→‎{{header|Java}}: Use library template for java.awt.Robot)
Task
Simulate input/Keyboard
You are encouraged to solve this task according to the task description, using any language you may know.

Send simulated keystrokes to a GUI window. You should specify whether the target may be externally created (i.e., if the keystrokes are going to an application other than the application that is creating them).

AutoHotkey

Target may be externally created. <lang AutoHotkey>run, cmd /k WinWait, ahk_class ConsoleWindowClass controlsend, ,hello console, ahk_class ConsoleWindowClass</lang>


C

Translation of: OCaml
Library: Xlib

Compile with:

gcc -o simkeypress -L/usr/X11R6/lib -lX11 simkeypress.c

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <X11/Xlib.h>
  3. include <X11/Xutil.h>

int main(int argc, char *argv[]) {

 Display *dpy;
 Window win;
 GC gc;
 int scr;
 Atom WM_DELETE_WINDOW;
 XEvent ev;
 XEvent ev2;
 KeySym keysym;
 int loop;
 /* open connection with the server */
 dpy = XOpenDisplay(NULL);
 if (dpy == NULL) {
   fputs("Cannot open display", stderr);
   exit(1);
 }
 scr = XDefaultScreen(dpy);
 /* create window */
 win = XCreateSimpleWindow(dpy,
         XRootWindow(dpy, scr),
         /* x, y, width, height, border_width */
         10, 10, 300, 200, 1,
         /* border, background */
         XBlackPixel(dpy, scr), XWhitePixel(dpy, scr));
 /* set window name */
 XStoreName(dpy, win, argv[0]);
 /* select kind of events we are interested in */
 XSelectInput(dpy, win, ExposureMask | KeyPressMask | ButtonPressMask);
 /* map (show) the window */
 XMapWindow(dpy, win);
 XFlush(dpy);
 /* default graphics context */
 gc = XDefaultGC(dpy, scr);
 /* connect the close button in the window handle */
 WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", True);
 XSetWMProtocols(dpy, win, &WM_DELETE_WINDOW, 1);
 /* event loop */
 loop = 1;
 while (loop) {
   XNextEvent(dpy, &ev);
   switch (ev.type)
   {
     case Expose:
       /* draw or redraw the window */
       {
         char msg1[] = "Clic in the window to generate";
         char msg2[] = "a key press event";
         XDrawString(dpy, win, gc, 10, 20, msg1, sizeof(msg1)-1);
         XDrawString(dpy, win, gc, 10, 35, msg2, sizeof(msg2)-1);
       }
       break;
     case ButtonPress:
       puts("ButtonPress event received");
       /*
       printf("> button (x,y) : %d %d\n",
              ev.xbutton.x,
              ev.xbutton.y);
       */
       ev2.type = KeyPress;
       ev2.xkey.state = ShiftMask;
       ev2.xkey.keycode = 24 + (rand() % 33);
       ev2.xkey.same_screen = True;
       XSendEvent(dpy, win, True/*propagate*/, KeyPressMask, &ev2);
       break;
  
     case ClientMessage:
       /* delete window event */
       if (ev.xclient.data.l[0] == WM_DELETE_WINDOW)
         loop = 0;
       break;
  
     case KeyPress:
       /* handle key press */
       puts("KeyPress event received");
       printf("> keycode: %d\n", ev.xkey.keycode);
       /* exit if q or escape are pressed */
       keysym = XLookupKeysym(&(ev.xkey), 0);
       if (keysym == XK_q ||
           keysym == XK_Escape) {
         loop = 0;
       } else {
         char buffer[] = "  ";
         int nchars = XLookupString(
               &(ev.xkey),
               buffer,
               2,  /* buffer size */
               &keysym,
               NULL );
         if (nchars == 1)
           printf("> Key '%c' pressed\n", buffer[0]);
       }
       break;
   }
 }
 XDestroyWindow(dpy, win);
 /* close connection to server */
 XCloseDisplay(dpy);
 return 1;

}</lang>

Java

Works with: Java version 1.5+

Keystrokes when this function is executed will go to whatever application has focus at the time. Special cases may need to be made for certain symbols, but most of the VK values in KeyEvent map to the ASCII values of characters. <lang java5>import java.awt.Robot public static void type(String str){

  Robot robot = new Robot();
  for(char ch:str.toCharArray()){
     if(Character.isUpperCase(ch)){
        robot.keyPress(KeyEvent.VK_SHIFT);
        robot.keyPress((int)ch);
        robot.keyRelease((int)ch);
        robot.keyRelease(KeyEvent.VK_SHIFT);
     }else{
        char upCh = Character.toUpperCase(ch);
        robot.keyPress((int)upCh);
        robot.keyRelease((int)upCh);
     }
  }

}</lang>

OCaml

Library: OCaml-Xlib

This example uses the Xlib to create a window, then when the user clics in this window an XKeyPressedEvent is sent with the function xSendEvent.

run with:

ocaml -I +Xlib Xlib.cma keysym.cma send_event.ml

<lang ocaml>open Xlib

let () =

 (* open connection with the server *)
 let d = xOpenDisplay "" in
 let s = xDefaultScreen d in
 Random.self_init();
 (* create window *)
 let w = xCreateSimpleWindow d (xRootWindow d s) 10 10 300 200 1
                               (xBlackPixel d s) (xWhitePixel d s) in
 (* set window name *)
 xStoreName d w Sys.argv.(0);
 (* select kind of events we are interested in *)
 xSelectInput d w [ExposureMask; KeyPressMask; ButtonPressMask];
 (* map (show) the window *)
 xMapWindow d w;
 xFlush d;
 let dbl = w in
 let gc = xDefaultGC d s in
 (* connect the close button in the window handle *)
 let wm_delete_window = xInternAtom d "WM_DELETE_WINDOW" true in
 xSetWMProtocols d w wm_delete_window 1;
 (* event loop *)
 let e = new_xEvent() in
 try while true do
   xNextEvent d e;
   (* draw or redraw the window *)
   match xEventKind e with
   | XExposeEvent _ ->
       xDrawString d dbl gc 10 20 "Clic in the window to generate";
       xDrawString d dbl gc 10 35 "a key press event";
   | XButtonPressedEvent event ->
       let dat = xButtonEvent_datas event in
       (*
       Printf.printf "button x,y : %d %d\n%!"
           dat.button_x
           dat.button_y;
       *)
       let xKeyEvent_contents = {
           key_serial     = dat.button_serial;
           key_send_event = dat.button_send_event;
           key_display    = dat.button_display;
           key_window     = dat.button_window;
           key_root       = dat.button_root;
           key_subwindow  = dat.button_subwindow;
           key_time       = dat.button_time;
           key_x          = dat.button_x;
           key_y          = dat.button_y;
           key_x_root     = dat.button_x_root;
           key_y_root     = dat.button_y_root;
           key_state = [ShiftMask];
           key_keycode = (24 + Random.int 33);
           key_same_screen = true;
       } in
       let propagate = true in
       let event_mask = KeyPressMask in
       xSendEvent d w propagate event_mask (XKeyPressedEvCnt xKeyEvent_contents);
   (* delete window event *)
   | XClientMessageEvent xclient ->
       let atom = xEvent_xclient_data xclient in
       if atom = wm_delete_window then
         raise Exit
   (* handle key press *)
   | XKeyPressedEvent event ->
       print_endline "Key Pressed Event";
       begin
         let d = xKeyEvent_datas event in
         Printf.printf "keycode: %d\n%!" d.key_keycode;
       end;
       (* exit if q or escape are pressed *)
       let keysym = xLookupKeysym event 0 in
       if keysym = Keysym.xK_q ||
          keysym = Keysym.xK_Escape then
         raise Exit
       else
         let printable, c =
           let buf = "  " in
           let n, _ = xLookupString event buf in
           if (n = 1)
           then (true, buf.[0])
           else (false, '\000')
         in
         if printable then
           Printf.printf "Key '%c' pressed\n%!" c;
   | _ -> ()
 done with
 | Exit ->
     xDestroyWindow d w;
     (* close connection to server *)
     xCloseDisplay d;
</lang>

Oz

Oz' default GUI toolkit is based on Tk. So we can do the same thing as in Tcl: <lang oz>declare

 [QTk] = {Module.link ['x-oz://system/wp/QTk.ozf']}
 Entry
 Window = {QTk.build td(entry(handle:Entry))}

in

 {Window show}
 {Entry getFocus(force:true)}
 for C in "Hello, world!" do
    Key = if C == 32 then "<space>" else [C] end
 in
    {Delay 100}
    {Tk.send event(generate Entry Key)}
 end</lang>

This only works with internal windows.

Tcl

Library: Tk

This only works with windows created by Tk; it sends a single key "x" to the given window. <lang tcl>set key "x" event generate $target <Key-$key></lang> To send multiple keys, call repeatedly in order. Alphabetic keys can be used directly as events, " " has to be mapped to "<space>". <lang Tcl>package require Tk pack [text .t] focus -force .t foreach c [split "hello world" ""] {

  event generate .t [expr {$c eq " "?"<space>": $c}]

}</lang> Note also that the task on keyboard macros illustrates a very closely related method.

VBScript

The keystrokes are sent to the active window.

<lang vbscript>Dim WshShell Set WshShell = WScript.CreateObject("WScript.Shell") WshShell.SendKeys "{Down}{F2}" WScript.Sleep 1000 ' one-second delay WshShell.SendKeys "{Left}{Left}{BkSp}{BkSp}Some text here.~" ' ~ -> Enter</lang>