Close
Showing results 1 to 9 of 9
  1. #1

    Default Multiboxing on Linux

    Hello,

    From looking through the forums I've gathered there a few multiboxers here running on Linux. So I'm hoping you'd be able to provide some information.
    I'm just trying to just get a basic key broadcast setup but it's proving rather difficult.
    I firstly though I'd use the xdotool to broadcast to all windows as it seemed to have the function to send keys to inactive windows. So I built my only application in Python to detect key presses and then launch a sub process for xdotool. However it seems I'm not able to get xdotool to send to any inactive window. It will send keys using xdotool key 1 but when the --window $name is added then nothing is sent. It is noted on the docs that this can occur and is just a limitation of the API. So I've been unable to find a solution around this. Nor can I see a python lib that has this feature.

    I've also tried programs through WINE such as
    AHK - Same issue, will not broadcast to background windows

    HKN - Seems to run but I cannot get any key broadcast

    ISboxer - Gives me a Direct3D error on launching Inner Space. Tried installing DX9 and .net 3.5 but still the same. Tried with DX 11 and DXVK but still the same message. With Dot Net 2.0 installed the ISBoxer toolkit works and will export to inner space find. However when launching the clients they show a black screen and then close after a second. Maybe related to the above, running wow without Inner space in the same prefix works fine.

    Any information on your linux setup's would be great. Thank you. I have checked Xlax but it seems to be for xterm windows and cannot find much good documentation for it.

  2. #2

    Default

    Hello,

    Few weeks ago I started to create a multiboxing software in C which supports keyboard and mouse (multipointer) broadcasting on GNU Linux, this project is still in early stages but main functions are implemented and I'am able to use it to gather with my little team I have made without any problems, so I think I could give you few tips in order to help you to progress.

    Let me quickly explain to you why you have this problem and how you could fix it, it needs a little bit of knowledge.

    First, when you try to send an event to a specific window, there are two methods to do it if you are running on a X Server (Xorg).

    Assuming you are using a C library called XCB to communicate with the X Server (there is also Xlib which has same functions but named differently and newer code should not use it, xdotool is using Xlib, you can check it here https://github.com/jordansissel/xdotool).

    -You can send an event with the xcb_send_event function which allows you to send an event to any window you have the ID plus some other parameters.
    -You can send an event with the xcb_test_fake_input function which is from the X11 XTest extension which allows you to send an event to the active window.

    The first function is clearly the best (it doesn't need to have the window activated or focused), but it has some limitations because a lot of programs are "blocking" these events mainly for security reasons (a third party program could use this function to write in your terminal, that is why a lot of softwares are usually blocking it).
    The second function is using a separate process to send events and the programs can't see if it is synthetic or not, so they don't block it but it is limited to the current active window, these events are the exact same events which are created when you physically type on your keyboard or move your mouse.

    Here we are, that is what xdotool is also using (there is only these two methods to send events to X11), xdotool will do the following :

    -If you are trying to send an event to a window which is not activated, xdotool will use the first function and it will fails if the window is blocking them (that is your current problem).
    -If you are trying to send an event to a window which is activated, it will use the second function and will always succeed if the XTest extension is present.

    As you can guess, these events won't pass through World of Warcraft instances if you are not focusing them but guess what, we can bypass that without breaking the rule!

    The solution is simply to use a Virtual Desktop in Wine for every WoW instance you are using (so if you have 5 different wow instances, you have to create 5 different wine prefix and to enable virtual desktop in winecfg for every prefix).

    Wine is not blocking the xcb_send_event function, it means you can send these events directly to wine without the need to have the virtual desktop focused.
    By using a virtual desktop and only launching one instance of the game per virtual desktop (= per WINEPREFIX), every WOW instance will always be focused inside this virtual desktop.

    The idea is simple :
    -(1) you send an event to the virtual desktop without the need to activate it
    -(2) the virtual desktop is not blocking these events, so it will give this event to the current active window inside this virtual desktop which is in this case the wow instance
    -(3) this results in wow getting the event

    Get it ? You only have to enable these "fake virtual desktop" on every wow prefix and then you will be able to use xdotool to send your events without the need to focus the windows.

    This was a bit long and maybe you are a bit lost, but now you have the explanation and the solution, feel free to ask questions.
    Last edited by intoxx : 03-15-2019 at 11:28 AM

  3. #3

    Default

    Intoxx,

    Thank you for your extensive response.

    After making this post I did look into how to resolve this myself and have been switching between trying to research how I can get Xdotool to work and switching between how I can get ISBoxer running on my Distro under Wine.

    As a result I have a much better understanding of how X11 is handling these events from my research and for a short while I did try playing with xdotool and WoW with the virtual desktop enabled However I wasn't successful at the time and didn't really spend much time checking it, as it was just guess work and I had no supporting information indicating that it would work.

    However now revisiting this, I've released why it wasn't working during my quick test.
    Previously I was using a command such as: xdotool search --name "WineDesktop" key 1. Something that according to the docs should send a key to all windows with the name. I assumed that would work because xdotool search --name "WineDesktop and xdotool selectwindow returned the same ID, so it would be the correct window.

    However after testing now, that wasn't working and so the solution I have found is using something like this:
    xdotool key --window $(xdotool search --pid $(pidof WoW.exe)) 1
    Maybe there is a more simple command but this does at least work so it's something I can refine.

    Currently I'm using my own small python project for key detection rather xkeybinds along with other small features that I've added for some quality of life improvements. Ofc it would be better in C for such an app but I just don't have a good enough knowledge yet of linux for something more low level. I would be interested through in checking this out, if your project is on Github?

    Thank you for taking the time to create your post, it has been very helpful, best regards.
    LightWrath
    Last edited by LightWrath : 03-15-2019 at 09:38 PM

  4. #4

    Default

    Hello,

    Good you found a way to make it work.
    At first I assumed you were getting correct window's id for your wow instances, apparently that was not the case, I should have checked.

    If you run one world of warcraft instance without the "virtual desktop" enabled in winecfg for this prefix, you can see the following :
    -xdotool search --name "World of Warcraft" : will return two window ID.
    -xdotool search --pid $(pidof Wow.exe) : will return three window ID.

    At least, that is what I get on my side.

    I recommend you to install the program "xwininfo" which is easy to use and will help you to figure out what window to use in the future.

    You said you were using xdotool key --window $(xdotool search --pid $(pidof Wow.exe)) which in that case should use the first ID in the list which is in my case the "Default IME" window (you can verify it by doing xwininfo -id $(xdotool search --pid $(pidof Wow.exe) | head -1) which should return you the name "Default IME").
    (xdotool take the first window in the list if there are many)

    So now, what window should you use, that is the question, "Default IME" is responsive of taking inputs so this should be enough, but if at some points you don't see that window, try using some other window ID, for myself, I use one of the window listed with xdotool search --name "World of Warcraft".

    I replicated what you were using and I couldn't send any inputs to other wow instances which were unactivated (without the need to enable "virtual desktop") like I said in my first post, so, here are my questions :


    -Are you sure you are now able to send inputs to other instances without the need to activate them ?
    -Are you launching all of these instances in the same or different wine prefixes ?
    -What version of wine are you using ?
    -Is the "virtual desktop" option disabled or enabled ?

    As for my sources, sadly they are not on Github and I'am keeping them for now, I'am waiting few months before releasing something to the public.

    But I can answer few questions if you have any, related to Linux/X11 if you are stuck at any point, don't hesitate to send me a private message.

    Intoxx.
    Last edited by intoxx : 03-16-2019 at 09:21 PM

  5. #5

    Default

    Hello Intoxx,
    Good to have your response again.

    Indeed it seems that I don't get the correct ID's when using the xdotool selectwindow function. When using getwindowname on the ID's from the pid it is, like your's, called "Default IME" but interestingly I wasn't able to send a key to all windows easy by just using 'xdotool key --window "Default IME" 1'.

    Your right in that my first post, the command would only send to a single window, I just had that for testing and proving that it worked. Since then, to send to all I've produced the following:

    In my small python application (That is detecting keys, conditions) I wanted to have a round-robin function. I know this can be achieved through wow macros but I never found that method to be that good. IE I always end up using 2 interrupts rather than one. And because my team is made of different classes I wanted the ability to use specific spells on a character but not needing a key for each one. So I wanted my app to be able to send a key to a specific window sometimes and other times to all. To save time I thought I'd just use the sub process module to pass xdotool commands to the system.

    Side Note: However it looks like I maybe better just using the xdo lib or xlib directly as doing the above means I have a config file full of huge commands and because variables aren't saved between calls they need to be declared each time.

    Because of this I wanted to match a character to a wow instance and this seemed difficult when I was only able to identify them by the PID. They all had the same window name but I could get xdotool to send an event to the window by using that. So the simple way I've found to overcome this is just copying 5 of the wow.exe's and renaming them. Now they can be targeted specifically. However it does mean I cannot target all and so for that I've only produced this: (See side note)
    declare -a allwow='([0]=$(pidof WoW.exe) [1]=$(pidof wow1.exe) [2]=$(pidof wow2.exe) [3]=$(pidof wow3.exe) [4]=$(pidof wow4.exe))' ; for ((i=0;i<"${#allwow[@]}";i++)); do xdotool key --window $(xdotool search --pid ${allwow[$i]}) b ; done

    So to answer your questions:
    1: Yes I can indeed to send events to windows without them being activated, but using xdotool, only 1 window at a time and so I've had to use a for loop to cycle windows
    2: These are all in different WINE PREFIX's
    3: Within the WINE PREFIX's they are using the latest version (I believe). So this is version 4.0 (DXVK 1.0.1)
    4: I am using the virtual desktop mode on all prefix's

    Well I'm very interested in your application, so if you do release it then I'd be interested in testing this.
    For now it seems to get the feature's I'm looking for I'll have to work further on my own small script/app

  6. #6

    Default

    You were using the virtual desktop mode, that is why it worked, good.

    Copying multiple Wow.exe is indeed a solution to your problem, but not a clean one.

    There are many possibilities to achieve what you want, the cleanest way is to get a list of every running X11 window and to go through the tree and find the correct windows.

    With xdotool alone, we can't really achieve that but we can use several tools to do it :


    1. You launch every wow instance with a unique virtual desktop name (see under to know how to do that).
    2. Copy the script I have made for you, save it as keybroadcast.py and make it executable with chmod +x <path_to_file>
    3. Edit the script where it is written "# RENAME ME", replace with your names, you can add more slave if you want, just follow the syntax (slave_1, slave_2 ...), it is a dictionnary.
    4. Execute it by invoking it with your terminal emulator or by passing it to the python3 interpreter, it doesn't need root privileges.


    Dependencies : python3, xwininfo (install them from your package manager)

    Code:
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # Author : Intoxx
    
    import subprocess
    from shlex import quote
    
    def execute(cmd):
        '''
        Execute a given command @cmd in a shell
        Return a list with the splitted result if there is any
        Else it will return None
        '''
        result = None
    
        try :
            out = subprocess.check_output(cmd, encoding="UTF-8", shell=True)
        except subprocess.CalledProcessError as error:
            pass
        else:
            out = out.split() # Clear some whitespace
            result = out if len(out) > 0 else None
    
        return result
    
    def find_window_by_name(name):
        result = execute("xdotool search --name {}".format(quote(name)))
    
        if result is not None:
            result = result[0] # result is a list, I only want the first element
    
        return result
    
    def find_child_window_by_name(parent, name):
            result = execute("xwininfo -children -id {} | grep {}".format(
                quote(parent), quote(name)))
    
            if result is not None:
                result = result[0] # result is a list, I only want the first element
    
            return result
    
    def send_key_press(window, key):
        execute("xdotool keydown --window {} {}".format(window, key))
    
    def send_key_release(window, key):
        execute("xdotool keyup --window {} {}".format(window, key))
    
    def send_key(window, key):
        execute("xdotool key --window {} {}".format(window, key))
    
    if __name__ == '__main__':
        '''
        This program will try to find every virtual desktop with their appropriate name,
        Then it will try to find inside each of them a window matching @IME_STRING variable
        And finally if everything has been found, it will send the key "1" to every instance
        '''
        IME_STRING = '"Default IME": ("wow.exe" "Wine")'
    
        instances = {
            "host" : {
                "virtual_desktop_name" : "WoW_1" # RENAME ME
            },
            "slave_1" : {
                "virtual_desktop_name" : "WoW_2" # RENAME ME TOO (add more if needed with unique identifiers)
            }
        }
    
        # Finding every instance for future usage
        for i in instances:
            # Find associated virtual desktop
            virtual_desktop = find_window_by_name(instances[i]["virtual_desktop_name"])
    
            if virtual_desktop is not None:
                # Found, save it
                instances[i]["virtual_desktop"] = virtual_desktop
                # Search for a child window named @IME_STRING
                default_ime = find_child_window_by_name(virtual_desktop,
                                                        IME_STRING)
    
                if default_ime is not None:
                    # Found, save it
                    instances[i]["default_ime"] = default_ime
                else:
                    print("Cannot find '{}' window for parent window '{}'".format(
                        IME_STRING, instances[i]["virtual_desktop_name"]))
            else:
                print("Cannot find '{}' virtual desktop".format(
                    instances[i]["virtual_desktop_name"]))
    
        # Using them
        for i in instances:
            # Sending "1" key to every instance (press followed by release)
            if instances[i]["default_ime"] is not None:
                    send_key(instances[i]["default_ime"], "1")
    This code is pretty easy and make use of xwininfo to get informations about every virtual desktop window then it parse the tree in order to get every Default_IME window, everything is saved in a dictionnary.
    Feel free to use it and add it to your current code.
    I didn't add a lot of comments but if you have any questions, just ask, start by trying it.
    (the magic part is in the usage of xwininfo combined with grep)
    Don't forget to edit the names for the "virtual_desktop_name" to be like your.

    If you don't want to use this python script, then you can simply open a terminal and write
    Code:
    xwininfo -children -id $(xdotool search --name "WoW_1") | grep '"Default IME": ("wow.exe" "Wine")' | awk '{print $1;}'
    This will print to you the window id of the "Default IME" window associated with the virtual desktop named "WoW_1".
    This also require awk tool which is usually already installed.

    How to launch a wine instance with a specific virtual desktop name ?
    Easy, edit the way you launch your wow instances with the following :
    Code:
    ............ wine explorer /desktop="WoW_1","1920x1080".........
    Documentation: https://wiki.winehq.org/Explorer

    When adding explorer /desktop="NAME","WIDTHxHEIGHT" to your wine command line, it will bypass what you set in your winecfg for the virtual desktop and will create it directly with a size of WIDTH x HEIGHT pixels and the title of the window will be NAME.

    Per example, my launching script is :

    Code:
    #!/bin/bash
    NAME="WoW_1" # Don't put any space
    RESOLUTION="1340x1080"
    PREFIX="/usr/local/data/wine/wow_1"
    WINEPREFIX=$PREFIX WINEDEBUG="-all" STAGING_SHARED_MEMORY=1 STAGING_WRITECOPY=1 wine explorer /desktop=$NAME,$RESOLUTION "$PREFIX/drive_c/Program Files (x86)/World of Warcraft/_retail_/Wow.exe"
    This is a simple way to launch several instances of the same game, just copy the file and change the name, the dimensions and the prefixe for every wow instance you have.

    You can take it if you want.

    Well, this should give you an idea how to do it without the need to rename all of these .exe.

    Intoxx.
    Last edited by intoxx : 03-17-2019 at 10:01 PM

  7. #7

    Default

    Hello Intoxx,

    Indeed for now the renaming the wow.exe is a temp solution so I can play with at least a working setup but indeed in the future I shall like to improve upon this and ofc your previous post will prove very helpful in achieving this.

    There are many options provided here. I think the Python script is overall most applicable to myself. As I'm already using python for my setup, it should be most seamless to just implement this into my current app. I'll have to try and find the time though as I plan on working on said python script to improve myself as well as making this implementation. Current my script is invoking system calls (sub process) while in the operation of sending keys, which means as I'm using xdotool also there is multiple system calls per execution. So in adding what you've written above I hope to remove some overhead.

    Thank you again for your valuable post.

    Regards
    LightWrath

  8. #8

    Default

    Been working on my own setup for boxing with wine, and while searching and troubleshooting some focus issues I ended up here.
    The wine virtual desktop and xtest combo works well, but if you're like me and use xfce you can quickly run into trouble.
    After much testing it turns out that xfce has a lot of weird behaviors regarding giving focus to windows, especially if the mouse is not within the bounds of that window.

    No matter what I tried, sending focus events, using wmctrl or xdotool, or even the built-in alt tab (cycle windows) and raising windows, if my cursor was not in the right place the focus would be lost, leaving the wine instances unresponsive to input. Tried with and without "focus follows cursor", same results.

    Finally randomly stumbled on the fact that just using Ctrl+Alt magically lets the window accept input again. This actually works without the cursor in the right place too. So, for anyone with similar focus problems while switching windows etc, you can try sending the following command to the "Default IME" id:
    Code:
    xdotool key --window _ID_HERE_ Ctrl+Alt
    As for the rest of my setup, it's a mess of combined scripts and tech, need to clean it up eventually before it can be seen in public.
    It removes window decorations/titlebar, moves/resizes the windows to fit in slots so that the screen is fully utilized, can move windows between slots, listens to global input and replicates to windows in slots that are not the current active one, etc.
    If anyone is stuck on such things give me a shout, I can give some pointers for now while I clean up the code.
    Right now I'm working on a small-scale version of how isboxer handles macros/targeting, trying to understand how best to generate and bind an addon with commands/macros.

    Anyway, enough rambling, hopefully this can help someone else in the future that have the same problems as I did.
    Last edited by Heff : 07-03-2019 at 06:37 PM

  9. #9

    Default

    I'd love to see some of the final products of the scripts you guys are working on, if anyone gets one done that they're willing to share.

    I've been fiddling with WoW and Linux on and off every now and then. I can get it to run, but I've always found that the performance is sub-par. One client runs well enough and I do expect some drop due to it not being native, so that's fine and dandy, but running 5 clients felt like it might be a stretch for my hardware, even when I can 10-box reasonably on Windows. I'm curious as to the performance you're experiencing as you're probably more adept at all of this than me and perhaps have done some optimization.

Tags for this Thread

Posting Rules

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •