./qjoypad-4.1.0/0000755000175000001440000000000011332170105012166 5ustar johnusers./qjoypad-4.1.0/README.txt0000644000175000001440000012174211315257566013715 0ustar johnusersQJoyPad 4 Documentation Distributed with QJoyPad 4.0; available online at http://qjoypad.sourceforge.net/doc/doc_index.html John Toman virtuoussin13@users.sourceforge.net http://qjoypad.sourceforge.net _________________________________________________________ Table of Contents 1. Introduction 1.1. What is QJoyPad? 1.2. What's it good for? 1.3. Features 2. Getting Started 2.1. Requirements 2.2. Installation 3. Using QJoyPad 3.1. The Tray Icon 3.2. The Popup Menu 3.3. The Setup Dialog 3.3.1. The Layout Selection combo box 3.3.2. The Add button 3.3.3. The Remove button 3.3.4. The Update button 3.3.5. The Revert button 3.3.6. The Joystick buttons 3.3.7. The Joystick Component buttons 3.3.8. The Clear button 3.3.9. Quick Set 3.4. Configuring axes 3.4.1. The Axis Position Indicator 3.4.2. Making an axis "Gradient" 3.4.3. Switching between keyboard and mouse control 3.4.4. Adjusting mouse speed 3.4.5. Setting keys 3.4.6. Throttle Settings 3.5. Configuring buttons 3.5.1. Choosing a key / mouse button 3.5.2. Making a button "Sticky" 3.5.3. Using Rapid Fire 3.6. Command-line use and scripting 4. Layout Files 5. Problems 5.1. I can't get my game controller to work in Linux; will QJoyPad help? 5.2. Joystick recognition 5.2.1. QJoyPad says it can't find any joysticks? 5.2.2. QJoyPad isn't showing all of my joysticks. 5.2.3. My joystick has more/fewer buttons/axes than that! 5.3. Joystick adjustment 5.3.1. Why does it say I'm moving if I'm not? 5.3.2. I keep going in two directions at once instead of just one! 5.3.3. I'm pushing up, but nothing's happening! 5.4. QJoyPad won't start! 5.5. I have two versions of QJoyPad open at once and they're getting in each other's way! 5.6. I'm getting strange errors when I change layouts; what's wrong? 5.7. This program only works in XWindows? 5.8. But my window manager doesn't HAVE a system tray! 5.9. I hate the QJoyPad icon. Is there any way to change it? 5.10. Why do I have to tell QJoyPad to "update joystick devices"? Why can't it do that on its own? 5.11. When QJoyPad checks for new joysticks, it doesn't find mine! 5.12. Why are both Up and Down treated as the same axis? 5.13. All of this is too complicated. Why isn't there a button for Up? 5.14. Features and suggestions 5.14.1. Why can't I click with an axis, or move the mouse with a button? 5.14.2. Why doesn't QJoyPad do _____? 6. Credits 7. This software is GPL! _________________________________________________________ Chapter 1. Introduction 1.1. What is QJoyPad? QJoyPad is a convenient little program with a QT interface that converts movement and button presses on a gamepad or joystick into key presses, mouse clicks, and mouse movement in XWindows. It should work on almost every Linux system and with any Linux-supported gaming device. _________________________________________________________ 1.2. What's it good for? QJoyPad lets you play any XWindows game that uses input from the keyboard and mouse with a joystick device, even if the game doesn't normally have joystick support. In addition, it's a convenient companion for emulation software as it prevents the need for extra controller plugins and can remember multiple saved layouts. Also, QJoyPad can quickly swap between layouts whenever you change games, so you'll always have the controls right where you want them instead of compromising for the game's defaults or the settings you find most useful in other games. Now with version 3, QJoyPad also supports features like rapid fire and sticky buttons (see Section 3.5.2) that can improve your gaming experience. Not a gamer? Then QJoyPad can still be pretty useful if you would find it more comfortable or convenient to control your computer with a joystick or game pad. It may be designed with gaming in mind, but it's a useful program for virtually any purpose. _________________________________________________________ 1.3. Features - Incorporates your gaming devices into any XWindows program - Move and click the mouse with your joystick - Auto-detects how many joysticks you have and how many buttons and axes each supports - Can detect joystick devices on the fly without restarting - Support for devices with more than two axes - Save as many layouts as you want and switch between them quickly - Swap layouts on the fly from the command line or from a script - Share layout files with your friends or even edit them by hand for greater control - Color cues quickly show you which buttons you're pressing and which joystick you're using - Set or reset all the keys at once in a flash - Adjust the sensitivity of every axis independently - Quietly hides in your system tray, running in the background without taking up space - For window managers without a system tray, QJoyPad can run without the tray icon. - Make an axis "Gradient" so that a light push does a little and a harder push does more - Support for throttle controls - Make a button "Sticky" if you don't feel like holding it down all the time - Turn on Rapid Fire so you don't wear out your gamepad! _________________________________________________________ Chapter 2. Getting Started 2.1. Requirements - A Linux computer and a Linux-compatible gaming device - A Linux kernel with joystick support (see the Linux Kernel HOWTO [http://www.linuxdocs.org/HOWTOs/Kernel-HOWTO.html] and the Linux joystick driver website http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/) - XWindows (see www.xfree86.org) - Trolltech's QT (see www.trolltech.com) _________________________________________________________ 2.2. Installation Installing QJoyPad should be a quick and painless process. The basic procedure is: ./config make make install however, there are some settings that might need to be changed. 1. Device directory: By default, QJoyPad will look for joystick devices in /dev/input, but if your system puts them somewhere else, you'll need to run ./config with the argument --devdir=DIR, where DIR is where your joystick devices reside. For instance, if your joystick devices are /dev/js0, /dev/js1, etc., instead of running ./config, run ./config --devdir="/dev" 2. Install directory: By default, QJoyPad will try to put a copy of itself in /usr/local/bin, some icons in /usr/local/share/pixmaps, and this readme in /usr/local/doc/qjoypad3 so that they will be accessible to all users. If you want these files to go somewhere other than /usr/local (or if you don't have permission to write to /usr/local), you'll need to pass the argument --prefix=DIR to ./config. For example, if you wanted to install QJoyPad just for yourself in your home directory, you could run ./config --prefix="/home/user" instead of ./config. 3. Use Plain Keys: Normally, QJoyPad doesn't use standard XWindows key names to describe your keyboard, but instead uses names that look nicer and are easier to recognize. For instance, instead of "KP_Begin", "Prior", and "Shift_L", QJoyPad uses "KP 5", "PageDown", and "L Shift". If you think generating these names is a waste of processor power, or if you don't think you're getting the right name for the key you're pressing, pass the argument --plain_keys to ./config and QJoyPad will just use the XWindows default names. Of course, you can mix use as many of these options at once as you like. For instance ./config --devdir="/dev" --prefix="/home/user" is completely valid. _________________________________________________________ Chapter 3. Using QJoyPad 3.1. The Tray Icon QJoyPad 3 is centered around the idea of a "tray icon", a little icon that usually sits on your taskbar, out of the way; when you first run QJoyPad, this is how it will be, just an icon. If your window manager doesn't support system tray icons, then you'll see QJoyPad as a tiny 24x24 window floating around like any other window (see Section 5.8 ). since this might be hard to work with, QJoyPad (starting with version 3.3) gives you the option of having a larger icon to work with; just run qjoypad --notray and QJoyPad will use a larger floating icon instead of a system tray icon. By right clicking on the QJoyPad icon (it should look like an old gamepad), you will get a pop-up menu that lets you switch layouts (when you first install QJoyPad, there will be no layouts available) and see some important information. To add or modify layouts, left click the icon to open the Setup Dialog. _________________________________________________________ 3.2. The Popup Menu When you right click the QJoyPad icon, a menu should pop up. The top of this menu shows you which joystick devices are currently available (see Section 5.2 if not all your devices are listed). Below that are the options to update the layout list or the joystick devices; use these if you have just put a new layout in ~/.qjoypad3 by hand or if you've plugged in a new joystick device. Below even farther is a list of the available layouts and the option to quit. _________________________________________________________ 3.3. The Setup Dialog The following sections describe the parts of the Setup Dialog going from the top down and from left to right. _________________________________________________________ 3.3.1. The Layout Selection combo box At the top of the Setup Dialog is a combo box that says [NO LAYOUT] to begin with, but as you add new layouts it will serve as a drop-down list of all the layouts available to you. _________________________________________________________ 3.3.2. The Add button The Add button adds a new layout to the list, asking you for a meaningful name. Make the name short, simple, and easy to remember, just in case you ever want to load the new layout from the command line. You can use any name you like that would be a legal filename on your computer (see Chapter 4 for details.). _________________________________________________________ 3.3.3. The Remove button The Remove button deletes the layout currently selected in the combo box, losing it forever. _________________________________________________________ 3.3.4. The Update button The Update button saves how the keys are currently set to the current layout. Use this to make changes to an already-defined layout. Don't forget to use Update to save any changes you make before quitting or switching to another layout, or all the changes will be forgotten! _________________________________________________________ 3.3.5. The Revert button The Revert button does about the opposite of Update. If you've gone and changed the keys around, pressing Revert will return them to how they are saved in the current layout. _________________________________________________________ 3.3.6. The Joystick buttons Immediately below the Add, Remove, Update, and Revert buttons, there are several buttons labeled Joystick 1, Joystick 2, etc. that serve as tabs so you can switch between different controllers to set keys. Whichever one of these buttons is pressed is the controller you are currently editing. Pressing any button or moving any axis on a given controller will make its associated button flash blue to let you know which it is. _________________________________________________________ 3.3.7. The Joystick Component buttons Beneath the Joystick Buttons is a large pile of buttons representing every axis and button on your controller. Whenever you move the axis or push the button that one of these buttons represents, it will flash blue so that you know which it is. To setup any of these, just click on the appropriate button and a dialog will pop up to let you choose your settings. _________________________________________________________ 3.3.8. The Clear button The Clear button resets all the axes and buttons to nothing, essentially rendering the joystick disabled. From here it's easy enough to set the buttons you need, starting from a clean slate. _________________________________________________________ 3.3.9. Quick Set The Quick Set button does exactly what you'd expect, it lets you set all the buttons and axes quickly! When you click it, it brings up a little window with a Done button, and until you click Done it's watching the controller. Whenever you press a button or move an axis, it will ask you which button you want associated with that movement. You can't set all the extra options this way, but it's much faster than going through all the dialogs! _________________________________________________________ 3.4. Configuring axes In QJoyPad 2, you were allowed one key to be assigned to each of four directions, Up, Down, Left, and Right. In version 3, there is support for many axes and each one can do fancier things than just press a key. Unfortunately, since different controllers do things differently, it's not as easy as Up, Down, Left, and Right. Up-Down is an axis, Left-Right is an axis, and if you have a nicer controller, you might have many more axes on top of that. The first step in configuring axes is to figure out which one you want to set. If you have a joystick, try moving it around and seeing which buttons flash blue on the QJoyPad Setup Dialog. If you have a gamepad, try pressing different buttons on the Directional-Pad or moving around any mini joystick controls it might have. Once you know which axis you want to set, click on its associated button to open the Set Axis dialog. _________________________________________________________ 3.4.1. The Axis Position Indicator In the middle of this dialog, you will see a white bar, divided in two, that represents the current position of the axis you're editing. Try moving that axis to see how it works. This is so you know which direction is considered "positive" and which is "negative"; it might not be what you'd expect. If this axis is a D-Pad, then it is either off or on, but most other axes are sensitive to how far they are depressed and a colored bar here will show you how far it is at the moment. Along the white bar, you will also see small blue and red tabs that you can drag. These adjust the "Dead Zone" and the "Extreme Zone" of the axis. When the colored bar representing the axis' position passes one of the blue markers, the bar will turn blue meaning that when the axis is this far QJoyPad will consider it moved, and when the bar passes one of the red markers it will turn red and QJoyPad will consider that axis fully depressed. When the bar is gray, that means that you haven't moved the axis out of its Dead Zone and QJoyPad is ignoring its movement. To adjust where the Dead and Extreme Zones are, just slide the blue and red markers to where you think they should be. You probably won't need to adjust the sensitivity unless you are having trouble getting QJoyPad to generate key presses when you want it to (see Section 5.3). _________________________________________________________ 3.4.2. Making an axis "Gradient" On the upper half of this dialog, you will see a checkbox marked Gradient. Checking this box means that instead of just generating one key press when the axis is moved, QJoyPad will start flickering that key on and off as soon as the axis is out of the Dead Zone (when the colored bar turns blue). How far the axis is pushed determines what percent of the time the simulated key will be depressed. As soon as the axis enters its Extreme Zone (when the colored bar turns red), the key will be down 100% of the time. Making an axis Gradient is useful if you want to use it as an accelerator in a game so how fast you go is controlled by how far the axis is moved. Also, it's nice to use this when the axis is set to move the mouse because it can give you finer control of the mouse when you push the axis just a little but still let you move quickly when you push it all the way. _________________________________________________________ 3.4.3. Switching between keyboard and mouse control On the upper half of the dialog, there is a combo box that lets you choose between keyboard control and mouse control. There are four different mouse options that let you choose whether the mouse will move vertically (Up-Down) when the axis moves or horizontally (Left-Right). You can also reverse the direction of the mouse if you want moving the axis up to move the mouse down or visa versa. Tip Usually you want an axis to be Gradient if it's going to move the mouse. _________________________________________________________ 3.4.4. Adjusting mouse speed When using one of the mouse modes, you can set the speed of the mouse by adjusting the number in the upper right corner. _________________________________________________________ 3.4.5. Setting keys When using keyboard mode, you can set which key corresponds to which direction of the axis by clicking the buttons immediately below the Axis Position Indicator. The one on the left will be pressed when the axis is moved in the negative direction (when the colored bar is on the left side) and the one on the right when it is in the positive direction (when the colored bar is on the right side). _________________________________________________________ 3.4.6. Throttle Settings Between these two buttons is another combo box that changes the throttle settings. This is meant for gamepads which have a specific type of throttle control. What it does is instead of having two keys for when the axis is positive or negative, it has just one and treats the way the axis moves differently. In one of the throttle modes, the axis will be considered centered when it is all the way to one direction or the other. _________________________________________________________ 3.5. Configuring buttons Similarly to the buttons corresponding to axes in the Setup Dialog, the ones corresponding to the buttons on your controller also light up to let you know which is which. To figure out which button you want, press it on the game device and then click on the button on screen that flashed. A small settings dialog will pop up. _________________________________________________________ 3.5.1. Choosing a key / mouse button At the top of this dialog is a button that you can click to set which key or mouse button you want to associate with this button on your controller. Just click on it, and the rest should be self-explanatory. _________________________________________________________ 3.5.2. Making a button "Sticky" Below this and to the left is a little checkbox marked Sticky. When a button is set as Sticky, that means that pressing the button once will make QJoyPad simulate a key press (or mouse button press) and pressing that button again will make QJoyPad release the simulated key. This is useful for racing games where you're almost always pouring on the gas, or for RPGs that have a button used for run, even though it's always better to be running. This way, all you have to do is press the button once and it's like you're holding it down. To let the button back up, just press it a second time. _________________________________________________________ 3.5.3. Using Rapid Fire Just next to the Sticky checkbox is another one marked Rapid Fire. When this is enabled for a button, holding that button down means that QJoyPad will flicker the associated key very fast. This is great for space shooters where you want to fire quickly but you don't want to break your button (or your thumb!) from pressing over and over again. Tip Keep in mind that any button can be set both Sticky AND Rapid Fire. This is even better for space shooters because this way all you need to do is press the button once and from then until you press it again you will be shooting Rapid Fire. _________________________________________________________ 3.6. Command-line use and scripting Although QJoyPad only works in XWindows, it supports changing axes on the fly from the command line. If you want to load up the layout named "Tetris", all you have to do is run: qjoypad "Tetris" and one of two things will happen. If QJoyPad isn't currently open, it will start running and load the "Tetris" layout (this is case sensitive! see Chapter 4). If QJoyPad is already running, it will just silently switch to the requested layout. What's so great about this is it lets you forget about QJoyPad once you've made all your layouts, and just worry about your games! It's very easy to write short little shell scripts to automatically load the layout you need when you start up a game. For instance, if you wanted to run the game xgalaga++, using QJoyPad for joystick support, you could create a text file called run-xgalaga with the following lines in it: #!/bin/sh qjoypad "XGalaga" & xgalaga++ Then with the command "chmod a+x run-xgalaga" you could make that text file an executable shell script; once that's done, all you need to to do is execute run-xgalaga and QJoyPad will load the appropriate layout and your game will start. To use this script for any other program, just change "XGalaga" to a different layout name and "xgalaga++" to the name of some other program and you're done. _________________________________________________________ Chapter 4. Layout Files When QJoyPad saves a layout, it creates a file using that layout's name. Because of this, layout names must also be valid filenames. This shouldn't be very limiting, it just means that names can't contain certain special characters such as '/', '*', etc. Remember that most Linux file systems are case sensitive, so a layout named "Layout" will be considered distinct from a layout named "layout". On most modern file systems, spaces should be okay and there should be no serious limits on name length. Whenever you create a new layout, QJoyPad makes a new file called Name.lyt in ~/.qjoypad3, where Name is the name that you provided. Whenever you update that layout, it overwrites that file to reflect your changes, whenever you revert, it rereads that file, and if you ever remove that layout, it will erase that file from your hard drive. The format of these files isn't difficult to figure out, so you can edit them by hand if you like. The numbers used to represent keys are standard X11 keycodes. It's also easy to share QJoyPad layout files; just copy them from one user's ~/.qjoypad3 directory to another and either tell QJoyPad to update the layout list by right clicking on the tray icon, or just restart QJoyPad. If you switch layouts through the command line, you don't even need to do that. _________________________________________________________ Chapter 5. Problems 5.1. I can't get my game controller to work in Linux; will QJoyPad help? Well, that depends on why you can't get it to work. For the most part, the answer is "No." QJoyPad can only use joysticks and gamepads that are recognized by your kernel and that have the proper drivers loaded. If you can't get your joysticks to work at all in Linux, then, no, QJoyPad can't help. (you might want to check out the joystick.txt file included with your kernel source; if you don't know anything about working with the kernel, check out the Linux Kernel HOWTO [http://www.linuxdocs.org/HOWTOs/Kernel-HOWTO.html] ) If your joystick is detected and somewhat working, but you can't get it to work in specific programs, then QJoyPad just might be what you're looking for. One of the main reasons I wrote QJoyPad was because my gamepads simply wouldn't work right with the input plugins for Linux Playstation emulators, so I know for a fact that sometimes QJoyPad can work around specific software issues. Check out Section 5.2 for some tips for checking if your joystick is working. _________________________________________________________ 5.2. Joystick recognition 5.2.1. QJoyPad says it can't find any joysticks? 5.2.2. QJoyPad isn't showing all of my joysticks. 5.2.3. My joystick has more/fewer buttons/axes than that! QJoyPad automatically recognizes your joysticks using the Linux joystick driver, so all joysticks must be working properly in Linux before they can be used in QJoyPad. If you can't see all of your joysticks or if QJoyPad complains it can't find any, chances are your joystick(s) are not plugged in or are not properly detected by Linux. If that's not it, QJoyPad could also be looking for your joysticks in the wrong directory. First, double check that your joysticks are plugged in. If they aren't, plug them, load any modules you might need, and tell QJoyPad to Update joystick devices with the popup menu (remember, this menu is only accessible when the Setup Dialog is closed). If you're still having trouble, QJoyPad might have been compiled with the devdir setting pointing to the wrong place. That option had to be set at compile time, and to change it you must recompile (see Section 2.2); however, if you don't want to bother with that, you can specify the location of your devices as an argument. Using the command "qjoypad --device /dev/input", for example, will start QJoyPad and tell it to look for joysticks in /dev/input/js0, /dev/input/js1, etc. If that doesn't work, then you might want to make sure your joysticks are working properly. One way to test this is to do a "cat /dev/input/js0" (or wherever your joystick device is) and press a few buttons on the controller. If you get a bunch of crazy characters, it's working. If not, you'll have to fiddle around with kernel drivers, and should probably look elsewhere for guidance. If for some reason QJoyPad is reporting the wrong number of buttons or axes for your device, that means the Linux joystick driver is also reporting the wrong number. Unless you can't get to buttons or axes that you need, this shouldn't be a problem, but if you want to get the number right, Try using a different driver or check out the documentation for the one you're using. If your joysticks are working, plugged in, and my program is looking in the right place, then I'm not sure what to tell you. Unfortunately, I don't have a wealth of different devices and software setups to test on. If you're really stuck, drop me a line and I'll see what I can do. _________________________________________________________ 5.3. Joystick adjustment 5.3.1. Why does it say I'm moving if I'm not? 5.3.2. I keep going in two directions at once instead of just one! 5.3.3. I'm pushing up, but nothing's happening! Chances are, this means you're using an overly sensitive or poorly calibrated joystick or your sensitivity settings are all wrong. Try adjusting the Dead Zone of the axes that are giving you trouble (move the blue tab in the Axis Edit dialog), more away from the center if it thinks you're pressing a button when you aren't, more toward the center if it thinks you aren't pressing a button when you are. If that doesn't work, try manually adjusting your joystick (if it has adjustment knobs/sliders), or try calibrating it with jscal. _________________________________________________________ 5.4. QJoyPad won't start! There are two reasons why QJoyPad won't start. For one, QJoyPad won't start is if it's already running! To make sure QJoyPad doesn't interfere with itself, only one version of QJoyPad is allowed to run at a time. If you can't see an already open version, look for the icon in the system tray. If you really can't find it anywhere, try running "killall qjoypad" and then "rm -f ~/lock.pid" and then try starting QJoyPad again. It should work this time. Finally, QJoyPad won't actually run if one of its arguments is --h or --help. When it sees one of those arguments, it outputs usage information to the console and then quits. If you're running QJoyPad away from a console or want it to run like normal, don't give one of these arguments. _________________________________________________________ 5.5. I have two versions of QJoyPad open at once and they're getting in each other's way! QJoyPad doesn't work well when there are two or more instances open; for that reason, it uses a file to tell whether or not it's already running. Every version of QJoyPad has done this, but in version 3.4, where that file is kept was changed to make the program more compatible with certain distributions. If you're seeing two versions of QJoyPad open at once, that means that either one of those is QJoyPad 3.4 and the other is an older version, or that you're running an older version of QJoyPad on a system where you don't have write access to /var/run. In either case, you should just make sure that you are running the newest version of QJoyPad and that there are no other versions installed on your system. If you really want to keep earlier versions of QJoyPad, that's fine! Just remember that if you do, it's possible to have two instancesrunning at once and that that can cause problems. _________________________________________________________ 5.6. I'm getting strange errors when I change layouts; what's wrong? Those errors show that there is something wrong with the layout files themselves. This means the files were corrupted, edited incorrectly, or for some reason QJoyPad didn't save them right (shouldn't ever happen, never seen it happen, but nothing's impossible). Unless the file QJoyPad is looking for is completely missing or mangled, it's quite likely that the file can be repaired by hand. If you need help with the save format, just send me an email and I'll see if I can't help. If worse comes to worst and you lose a layout file you created, it shouldn't take you too long to rebuild it from scratch. _________________________________________________________ 5.7. This program only works in XWindows? Yep, I'm afraid so. For all of you out there with old Linux console games that won't run in an xterm, you'll have to try something else. If you really must find a way, joy2key is a program that is similar to QJoyPad but without a graphical interface or many of the fancier features, but which doesn't have that limitation. Check it out at: http://interreality.org/~tetron/technology/joy2key. _________________________________________________________ 5.8. But my window manager doesn't HAVE a system tray! I'm well aware that every Linux setup is different and that there are a million different window managers that range from beautiful, feature-full, and bloated to stark, minimalist, and lightning-fast. Unfortunately, after a few people suggested that I have a tray icon for the no-gui mode, I realized that it was a very, very good idea. The new version of QJoyPad is built up around the system tray conceptually, and to make a version that doesn't use it would be a lot of work, so for now I plan to keep things as they are. However, to accommodate those of you who don't have a system tray and can't stand that little icon, using the argument --notray makes a floating icon that is much bigger instead of the little tray icon. It still behaves exactly as the smaller icon would, except it is larger and cannot be captured by the system tray. _________________________________________________________ 5.9. I hate the QJoyPad icon. Is there any way to change it? Absolutely! Starting with version 3.3, QJoyPad stores its icons in $PATH/share/pixmaps/qjoypad (where $PATH is the install path used by given at install time, /usr/local by default) and actually comes with a few different icons to choose from. In that directory, there are two files, icon24.png and icon64.png which correspond to the small and large icons that qjoypad will use. These files are just links, and can be relinked to any png image of the appropriate size. Feel free to switch between the provided icons, change them, or make your own! QJoyPad should look how you want it to. _________________________________________________________ 5.10. Why do I have to tell QJoyPad to "update joystick devices"? Why can't it do that on its own? It can! With the hotplug options of the more recent Linux kernels, not only is it easy to automatically load the right modules for a joystick when it is plugged in, but also to notify QJoyPad and have it update its list. Unfortunately, this is a little complex, still not available on everyone's computer, and still in development. If you'd like to get this setup, I'd love to help you figure things out and perhaps I'll make it a standard feature of QJoyPad once I have a better idea of what needs to be done on various systems. Please, either contact me for help getting started, or send me an email after you get it working explaining how you did it ;) To make QJoyPad search for new devices, use the command qjoypad --update. _________________________________________________________ 5.11. When QJoyPad checks for new joysticks, it doesn't find mine! When you plug in a joystick, there are certain things that have to happen for the joystick to become available to programs like QJoyPad. Mainly, the joystick has to be recognized and drivers have to be loaded. Even if this process is automatic on your computer it could take a few seconds, so if QJoyPad can't find your device right away, try again a few moments later. If driver modules aren't loaded automatically, don't forget to load them before you ask QJoyPad to look for new devices. If you keep having troubles, see Section 5.2. _________________________________________________________ 5.12. Why are both Up and Down treated as the same axis? That's because they are the same axis. An "axis" on a joystick (or gamepad) isn't actually a direction, but a dimension. A standard simple joystick can move along two axes, the X-axis (side to side) and the Y-axis (up and down); when you move the stick to the left, you're moving it along the X-axis in the negative direction, and when you move it to the right you're moving it in the positive direction along the same axis. What really matters is that in QJoyPad, every axis represents two opposing directions and can therefore have two different keys. I only do it that way because thats how the device itself works. I'd make the labels a little more intuitive, but unfortunately what each axis corresponds to changes from controller to controller and there's no way for me to know which is which. If you don't know which axis to set for which direction, move in the direction you want and see which button lights up, or try using Quick Set instead. _________________________________________________________ 5.13. All of this is too complicated. Why isn't there a button for Up? Unfortunately, adding new features means increasing complexity and making things more confusing. That's just how things go. If you just want to have one key pressed when you press a button on your joystick, try using just the quick set feature of QJoyPad 3. There all you need to do is press what you want to press on the joystick and then type the key you want that button to trigger. Also, if you preferred the simplicity of QJoyPad 2.1, it's still available and quite functional, it just doesn't have quite as many options and doesn't use a system tray icon. The two versions of QJoyPad are compatible and can both be run on the same computer without getting in each others' way (as long as you rename one of them so they aren't both called "qjoypad"), just not at the same time. _________________________________________________________ 5.14. Features and suggestions 5.14.1. Why can't I click with an axis, or move the mouse with a _________________________________________________________ 5.14.2. Why doesn't QJoyPad do _____? For the sake of my sanity, I didn't program every possible thing I could imagine someone wanting to do into QJoyPad. I added in the features that people were asking for and which made sense, and I set somewhat arbitrary limits on what the user can and can't do. Why set limits? Because if I didn't the program would get far too bulky and too time consuming to write. I tried to draw the line at what I thought was reasonable use. No, you can't make the mouse click whenever you move an axis... but why would you want to? If there's something that you feel QJoyPad should be able to do that it can't, let me know and I'll considering adding that in future versions. _________________________________________________________ Chapter 6. Credits Thank you to Erich Kitzmlller, author of xjoypad for the inspiration to write QJoyPad and for the code that started me off. The development team for Psi, the Jabber client, also get a lot of thanks for writing the tray icon code that I borrowed and tweaked. Thank you for developing GPL and for helping other developers! (Check out the Psi Website [http://psi.affinix.com/]) Thank you also to everyone who has sent me an email about QJoyPad. Knowing that my program is used and appreciated means a lot, especially since that's about all I get out of my programming. Open source is like teaching; it's very important and means a lot for young and developing programmers, but it's a time consuming and underpaid profession ;) Finally, I need to offer a very warm thank you to Mark Hannessen who graciously donated one Logitech Wingman Rumblepad to the cause of QJoyPad. Without that, I simply would not have been able to add support for multiple axes or throttle controls, so version 3 might have never been made. Thank you for your interest and for your support, Mark. _________________________________________________________ Chapter 7. This software is GPL! Check out LICENSE.txt or http://www.gnu.org/licenses/gpl.txt for details! The bare bones of it is, this program is distributed in open-source form so that others may share it freely, learn from it, or modify it as they see fit. However, under no circumstances can it be sold! This code was written entirely by Nathan Gaylinn (excepting the code used for displaying a tray icon that is adapted from the Psi source [http://psi.affinix.com/]) but is based on the idea of xjoypad by Erich Kitzmller. Yay for the power GPL gives to developers! ./qjoypad-4.1.0/LICENSE.txt0000644000175000001440000004313111160766732014033 0ustar johnusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ./qjoypad-4.1.0/src/0000755000175000001440000000000011332170104012754 5ustar johnusers./qjoypad-4.1.0/src/event.h0000644000175000001440000000076111160766732014273 0ustar johnusers#ifndef JEVENT_H #define JEVENT_H //for the functions we need to generate keypresses / mouse actions #include //types of events QJoyPad can create. //KeyRelease, KeyPress, ButtonRelease, ButtonPress, and MouseMove enum eventType {KREL, KPRESS, BREL, BPRESS, WARP}; //a simplified event structure that can handle buttons and mouse movements struct xevent { eventType type; int value1; //button, keycode, or x int value2; //y }; void sendevent( xevent e ); #endif ./qjoypad-4.1.0/src/keycode.cpp0000644000175000001440000000672111235433371015123 0ustar johnusers#include "keycode.h" #include "getkey.h" const QString ktos( int keycode ) { if (keycode > MAXKEY || keycode < 0) keycode = 0; if (keycode == 0) return "[NO KEY]"; QString xname = XKeysymToString(XKeycodeToKeysym(display, keycode,0)); //this section of code converts standard X11 keynames into much nicer names //which are prettier, fit the dialogs better, and are more readily understandable. //This is really ugly and I wish I didn't have to do this... that's why there //is a config option to define PLAIN_KEYS and drop this whole section of code, //instead using the default names for keys. #ifndef PLAIN_KEYS //the following code assumes xname is system independent and always //in the same exact format. QRegExp rx; rx.setPattern("^\\w$"); //"a-z" -> "A-Z" if (rx.exactMatch(xname)) return xname.toUpper(); rx.setPattern("(.*)_(.*)"); if (rx.exactMatch(xname)) { QString first = rx.cap(1); QString second = rx.cap(2); rx.setPattern("^[RL]$"); //"Control_R" -> "R Control" if (rx.exactMatch(second)) return second + " " + first; rx.setPattern("^(Lock|Enter)$"); //"Caps_Lock" -> "Caps Lock" //"KP_Enter" -> "KP Enter" if (rx.exactMatch(second)) return first + " " + second; //the following assumes all number pads are laid out alike. if (xname == "KP_Home") return "KP 7"; if (xname == "KP_Up") return "KP 8"; if (xname == "KP_Prior") return "KP 9"; if (xname == "KP_Subtract") return "KP -"; if (xname == "KP_Left") return "KP 4"; if (xname == "KP_Begin") return "KP 5"; if (xname == "KP_Right") return "KP 6"; if (xname == "KP_Add") return "KP +"; if (xname == "KP_End") return "KP 1"; if (xname == "KP_Down") return "KP 2"; if (xname == "KP_Next") return "KP 3"; if (xname == "KP_Insert") return "KP 0"; if (xname == "KP_Delete") return "KP ."; if (xname == "KP_Multiply") return "KP *"; if (xname == "KP_Divide") return "KP /"; return xname; } if (xname == "minus") return "-"; if (xname == "equal") return "="; if (xname == "bracketleft") return "["; if (xname == "bracketright") return "]"; if (xname == "semicolon") return ";"; if (xname == "apostrophe") return "'"; if (xname == "grave") return "`"; if (xname == "backslash") return "\\"; if (xname == "comma") return ","; if (xname == "period") return "."; if (xname == "slash") return "/"; if (xname == "space") return "Space"; if (xname == "Prior") return "PageUp"; if (xname == "Next") return "PageDown"; #endif //if none of that succeeded, return xname; } KeyButton::KeyButton( QString name, int val, QWidget* parent, bool m, bool nowMouse) :QPushButton(nowMouse?"Mouse " + QString::number(val):QString(ktos(val)), parent) { mouse = m; mouseClicked = nowMouse; buttonname = name; value = val; connect( this, SIGNAL( clicked() ), SLOT( onClick() )); } void KeyButton::onClick() { //when clicked, ask for a key! value = GetKey( buttonname, mouse ).exec(); //if the return value was a mouse click... if (value > MOUSE_OFFSET) { mouseClicked = true; value -= MOUSE_OFFSET; setText( "Mouse " + QString::number(value)); } //otherwise, it was a key press! else { mouseClicked = false; setText( ktos(value)); } } ./qjoypad-4.1.0/src/axis_edit.cpp0000644000175000001440000001423111254060314015436 0ustar johnusers#include "axis_edit.h" AxisEdit::AxisEdit( Axis* ax ) :QDialog() { //build the dialog, display current axis settings :) axis = ax; setWindowTitle("Set " + axis->getName()); setWindowIcon(QPixmap(ICON24)); //h, v, and v2 are all references to layouts. They are used to refer to //various layouts as the dialog is built and are not pointing to the same //thing throughout. This is just because I don't care about the layouts //after I have placed the widgets within them and there's no reasno to //keep track of them. QVBoxLayout* v = new QVBoxLayout(this); v->setMargin(5); v->setSpacing(5); QHBoxLayout* h = new QHBoxLayout(); QVBoxLayout* v2 = new QVBoxLayout(); v2->setMargin(5); v2->setSpacing(5); CGradient = new QCheckBox("Gradient", this); CGradient->setChecked(axis->gradient); connect(CGradient, SIGNAL(toggled(bool)), this, SLOT( CGradientChanged( bool ))); v2->addWidget(CGradient); CMode = new QComboBox(this); CMode->insertItem((int)keybd, QString("Keyboard"), Qt::DisplayRole); CMode->insertItem((int) mousepv,QString("Mouse (Vert.)"),Qt::DisplayRole); CMode->insertItem((int) mousenv, QString("Mouse (Vert. Rev.)"), Qt::DisplayRole); CMode->insertItem((int) mouseph, "Mouse (Hor.)", Qt::DisplayRole); CMode->insertItem((int)mousenh, QString("Mouse (Hor. Rev.)"), Qt::DisplayRole); CMode->setCurrentIndex( axis->mode ); connect(CMode, SIGNAL(activated(int)), this, SLOT( CModeChanged( int ))); v2->addWidget(CMode); CTransferCurve = new QComboBox(this); CTransferCurve->insertItem(linear, QString("Linear"), Qt::DisplayRole); CTransferCurve->insertItem(quadratic, QString("Quadratic"),Qt::DisplayRole ); CTransferCurve->insertItem(cubic, QString("Cubic"),Qt::DisplayRole ); CTransferCurve->insertItem(quadratic_extreme, QString("Quadratic Extreme"), Qt::DisplayRole); CTransferCurve->insertItem(power_function, QString("Power Function"), Qt::DisplayRole); CTransferCurve->setCurrentIndex( axis->transferCurve ); CTransferCurve->setEnabled(axis->gradient); connect(CTransferCurve, SIGNAL(activated(int)), this, SLOT( CTransferCurveChanged( int ))); v2->addWidget(CTransferCurve); h->addLayout(v2); MouseBox = new QFrame(this); MouseBox->setFrameStyle( QFrame::Box | QFrame::Sunken ); v2 = new QVBoxLayout(MouseBox); v2->setSpacing(5); v2->setMargin(5); //v2->setAutoAdd(true); QLabel *mouseLabel = new QLabel("Mouse Speed", MouseBox); v2->addWidget(mouseLabel); SSpeed = new QSpinBox(MouseBox); SSpeed->setRange(0,MAXMOUSESPEED); SSpeed->setSingleStep(1); SSpeed->setValue(axis->maxSpeed); v2->addWidget(SSpeed); LSensitivity = new QLabel("Sensitivity", MouseBox); v2->addWidget(LSensitivity); SSensitivity = new QDoubleSpinBox(MouseBox); SSensitivity->setRange(1e-3F, 1e+3F); SSensitivity->setSingleStep(0.10); SSensitivity->setValue(axis->sensitivity); v2->addWidget(SSensitivity); h->addWidget(MouseBox); v->addLayout(h); Slider = new JoySlider(axis->dZone, axis->xZone, axis->state, this); v->addWidget(Slider); KeyBox = new QFrame(this); KeyBox->setFrameStyle( QFrame::Box | QFrame::Sunken ); h = new QHBoxLayout(KeyBox); h->setSpacing(5); h->setMargin(5); //h->setAutoAdd(true); BNeg = new KeyButton(axis->getName(),axis->nkeycode,KeyBox); CThrottle = new QComboBox(KeyBox); CThrottle->insertItem(0,"Neg. Throttle",Qt::DisplayRole); CThrottle->insertItem(1,"No Throttle",Qt::DisplayRole); CThrottle->insertItem(2,"Pos. Throttle",Qt::DisplayRole); CThrottle->setCurrentIndex(axis->throttle + 1); connect( CThrottle, SIGNAL( activated( int )), this, SLOT( CThrottleChanged( int ))); BPos = new KeyButton(axis->getName(),axis->pkeycode,KeyBox); h->addWidget(BNeg); h->addWidget(CThrottle); h->addWidget(BPos); v->addWidget( KeyBox ); h = new QHBoxLayout(); BOkay = new QPushButton("Okay", this); connect(BOkay, SIGNAL( clicked() ), this, SLOT( accept())); h->addWidget(BOkay); BCancel = new QPushButton("Cancel", this); connect(BCancel, SIGNAL( clicked() ), this, SLOT( reject())); h->addWidget(BCancel); v->addLayout(h); CModeChanged( axis->mode ); CTransferCurveChanged( axis->transferCurve ); CThrottleChanged( axis->throttle + 1 ); } void AxisEdit::show() { QDialog::show(); setFixedSize(size()); } void AxisEdit::setState( int val ) { Slider->setValue( val ); } void AxisEdit::CGradientChanged( bool on ) { CTransferCurve->setEnabled(on); if (on) { CTransferCurveChanged( axis->transferCurve ); } else { LSensitivity->setEnabled(false); SSensitivity->setEnabled(false); } } void AxisEdit::CModeChanged( int index ) { if (index == keybd) { MouseBox->setEnabled(false); KeyBox->setEnabled(true); } else { MouseBox->setEnabled(true); KeyBox->setEnabled(false); if (CGradient->isChecked()) { CTransferCurve->setEnabled(true); CTransferCurveChanged( axis->transferCurve ); } } } void AxisEdit::CTransferCurveChanged( int index ) { if (index == power_function) { LSensitivity->setEnabled(true); SSensitivity->setEnabled(true); } else { LSensitivity->setEnabled(false); SSensitivity->setEnabled(false); } } void AxisEdit::CThrottleChanged( int index ) { switch (index) { case 0: BNeg->setEnabled(true); BPos->setEnabled(false); break; case 1: BNeg->setEnabled(true); BPos->setEnabled(true); break; case 2: BNeg->setEnabled(false); BPos->setEnabled(true); break; } Slider->setThrottle( index - 1 ); } void AxisEdit::accept() { axis->gradient = CGradient->isChecked(); axis->maxSpeed = SSpeed->value(); axis->transferCurve = (TransferCurve)CTransferCurve->currentIndex(); axis->sensitivity = SSensitivity->value(); axis->throttle = CThrottle->currentIndex() - 1; axis->dZone = Slider->dZone(); axis->xZone = Slider->xZone(); axis->mode = (AxisMode) CMode->currentIndex(); axis->pkeycode = BPos->getValue(); axis->nkeycode = BNeg->getValue(); axis->adjustGradient(); QDialog::accept(); } ./qjoypad-4.1.0/src/layout_edit.cpp0000644000175000001440000001372611311571343016022 0ustar johnusers#include "layout_edit.h" //build the dialog LayoutEdit::LayoutEdit( LayoutManager* l ): QWidget(NULL) { lm = l; setAttribute(Qt::WA_DeleteOnClose); setWindowTitle( NAME ); setWindowIcon(QPixmap(ICON24)); LMain = new QVBoxLayout( this); LMain->setSpacing(5); LMain->setMargin(5); QFrame* frame = new QFrame(this); frame->setFrameStyle(QFrame::Box | QFrame::Sunken ); QGridLayout* g = new QGridLayout(frame); g->setMargin(5); g->setSpacing(5); CLayouts = new QComboBox(frame); connect( CLayouts, SIGNAL(activated( const QString& )), lm, SLOT(load(const QString&))); g->addWidget(CLayouts,0,0,1,4); //most of these buttons can link directly into slots in the LayoutManager BAdd = new QPushButton("Add", frame); connect(BAdd, SIGNAL(clicked()), lm, SLOT(saveAs())); g->addWidget(BAdd,1,0); BRem = new QPushButton("Remove", frame); connect(BRem, SIGNAL(clicked()), lm, SLOT(remove())); g->addWidget(BRem,1,1); BUpd = new QPushButton("Update", frame); connect(BUpd, SIGNAL(clicked()), lm, SLOT(save())); g->addWidget(BUpd,1,2); BRev = new QPushButton("Revert", frame); connect(BRev, SIGNAL(clicked()), lm, SLOT(reload())); g->addWidget(BRev,1,3); LMain->addWidget( frame ); //produce a list of names for the FlashRadioArray //this is only necesary since joystick devices need not always be //contiguous int padcount = available.count(); QString names[padcount]; int i = 0; do { QHashIterator it( available ); while (it.hasNext()) { it.next(); names[i] = it.value()->getName(); connect(this, SIGNAL(focusStateChanged(bool)), it.value(), SLOT(focusChange(bool))); ++i; } } while (0); //flash radio array JoyButtons = new FlashRadioArray(padcount, names, true, this ); LMain->addWidget( JoyButtons ); //we have a WidgetStack to represent the multiple joypads PadStack = new QStackedWidget( this ); PadStack->setFrameStyle(QFrame::Box | QFrame::Sunken ); LMain->addWidget(PadStack); //go through each of the available joysticks i = 0; // i is the current index into PadStack do { QHashIterator it(available); while (it.hasNext()) { it.next(); //add a new JoyPadWidget to the stack PadStack->insertWidget( i,it.value()->widget(PadStack,i) ); //every time it "flashes", flash the associated tab. connect( PadStack->widget(i), SIGNAL( flashed( int ) ), JoyButtons, SLOT( flash( int ))); ++i; } } while (0); //whenever a new tab is selected, raise the appropriate JoyPadWidget connect( JoyButtons, SIGNAL( changed( int ) ), PadStack, SLOT( setCurrentIndex( int ))); updateLayoutList(); //add the buttons at the bottom. QHBoxLayout* h = new QHBoxLayout(0); h->setMargin(0); h->setSpacing(5); QPushButton* close = new QPushButton( "-- Close Dialog --", this ); connect(close, SIGNAL(clicked()), this, SLOT(close())); h->addWidget(close); QPushButton* quit = new QPushButton( "-- Quit --", this ); connect( quit, SIGNAL( clicked() ), qApp, SLOT(quit())); h->addWidget(quit); LMain->addLayout(h); connect(qApp, SIGNAL(focusChanged ( QWidget * , QWidget * ) ), this, SLOT(appFocusChanged(QWidget *, QWidget *))); this->show(); } void LayoutEdit::setLayout(QString layout) { //change the text, CLayouts->setCurrentIndex(lm->getLayoutNames().indexOf(layout)); //update all the JoyPadWidgets. for (uint i = 0; i < available.count(); i++) { ((JoyPadWidget*)PadStack->widget(i))->update(); } } void LayoutEdit::updateLayoutList() { //blank the list, then load in new names from the LayoutManager. CLayouts->clear(); QStringList layouts = lm->getLayoutNames(); CLayouts->insertItems(-1, layouts); CLayouts->setCurrentIndex(layouts.indexOf(lm->CurrentLayout)); } void LayoutEdit::updateJoypadWidgets() { int indexOfFlashRadio = LMain->indexOf(JoyButtons); FlashRadioArray *newJoyButtons; int padcount = available.count(); QString names[padcount]; int i = 0; do { QHashIterator it( available ); while (it.hasNext()) { it.next(); names[i] = it.value()->getName(); ++i; } } while (0); newJoyButtons = new FlashRadioArray(padcount, names, true, this ); LMain->insertWidget(indexOfFlashRadio, newJoyButtons); LMain->removeWidget(JoyButtons); FlashRadioArray* oldJoyButtons = JoyButtons; JoyButtons = newJoyButtons; connect( JoyButtons, SIGNAL( changed( int ) ), PadStack, SLOT( setCurrentIndex( int ))); oldJoyButtons->deleteLater(); int numberOfJoypads = PadStack->count(); for(int i = 0; iremoveWidget(PadStack->widget(0)); } i = 0; do { QHashIterator it(available); while (it.hasNext()) { it.next(); //add a new JoyPadWidget to the stack PadStack->insertWidget( i,it.value()->widget(PadStack,i) ); //every time it "flashes", flash the associated tab. connect( PadStack->widget(i), SIGNAL( flashed( int ) ), JoyButtons, SLOT( flash( int ))); ++i; } } while (0); } void LayoutEdit::closeEvent(QCloseEvent *event) { lm->leWindowClosed(); event->accept(); } void LayoutEdit::appFocusChanged(QWidget *old, QWidget *now) { if(now!=NULL && old==NULL) { emit focusStateChanged(false); } else if(old!=NULL && now==NULL) { emit focusStateChanged(true); QHashIterator it( available ); while (it.hasNext()) { debug_mesg("iterating and releasing\n"); it.next(); it.value()->release(); } debug_mesg("done releasing!\n"); } } ./qjoypad-4.1.0/src/buttonw.cpp0000644000175000001440000000106011235433371015171 0ustar johnusers#include "buttonw.h" ButtonWidget::ButtonWidget( Button* b, QWidget* parent ) : FlashButton( "", parent) { button = b; update(); on = false; } void ButtonWidget::jsevent( int val ) { bool newOn = (val == 1); if (on != newOn) { on = newOn; flash(); } } void ButtonWidget::update() { setText( button->status()); } void ButtonWidget::mouseReleaseEvent( QMouseEvent* e ) { ButtonEdit* be = new ButtonEdit(button); be->exec(); delete be; update(); FlashButton::mouseReleaseEvent( e ); } ./qjoypad-4.1.0/src/joyslider.h0000644000175000001440000000344011235433371015144 0ustar johnusers#ifndef Q_JOYSLIDER_H #define Q_JOYSLIDER_H //the parent of this #include #include #include #include #include #include #include #include #include #include #include "constant.h" //dragging constants. //When dragging == DRAG_XZ, that means we are currently dragging the xZone mark #define DRAG_XZ 1 //When dragging == DRAG_DZ, that means we are currently dragging the dZone mark #define DRAG_DZ 2 class JoySlider : public QWidget { public: JoySlider( int dz, int xz, int val, QWidget* parent ); //set where the axis physically is void setValue( int ); //change the throttle mode void setThrottle( int ); //get the current settings int dZone() { return DeadZone; }; int xZone() { return XZone; }; protected: //all for getting the widget to look right: void drawBox( int x, int width ); void paintEvent( QPaintEvent* ); void resizeEvent( QResizeEvent* ); //for working with drag and drop: void mousePressEvent( QMouseEvent* e ); void mouseReleaseEvent( QMouseEvent* ); void mouseMoveEvent( QMouseEvent* e ); private: //dimensions of the box to draw! int boxwidth; int boxheight; int rboxstart; int rboxend; int lboxstart; int lboxend; int twidth; int tend; //the throttle mode int throttle; //convert from joystick value to pixel position on the widget //negative refers to whether or not we're on the "negative" end //of the widget int pointFor( int value, bool negative ); //convert from pixel position to joystick value int valueFrom( int point ); //the current drag and drop state int dragging; //the axis' position int JoyVal; //the dead and extreme zone values int DeadZone; int XZone; }; #endif ./qjoypad-4.1.0/src/joypadw.h0000644000175000001440000000357311235433371014624 0ustar johnusers#ifndef JOYPAD_WIDGET_H #define JOYPAD_WIDGET_H //parts for the widget //Added by qt3to4: #include #include "axisw.h" //this all relates to a JoyPad #include "joypad.h" //and a JoyPadWidget is composed of AxisWidgets and ButtonWidgets #include "buttonw.h" //JoyPadWidget also is what initiates the whole QuickSet procedure :) #include "quickset.h" //because of some circularity issues, I need to forward declare these. class JoyPad; class QuickSet; //Widget for editing a JoyPad class JoyPadWidget : public QWidget { Q_OBJECT public: JoyPadWidget( JoyPad* jp, int i, QWidget* parent); ~JoyPadWidget(); //takes in an event and decides whether or not to flash anything void jsevent( js_event msg ); public slots: //called whenever one of the subwidgets flashes... used to determine //when to emit the flashed() signal. void flash( bool on ); //update all the components, that is, get the proper labels up on the buttons void update(); //reset everything void clear(); //quickset! void setAll(); signals: //happens whenever the tab that represents this joypadwidget should flash //(either on or off) The int is the index of this widget so that this //signal can be directly connected to FlashRadioArray's flash(int) void flashed(int); protected: //the joypad this is linked to JoyPad* joypad; //the index of this widget IN THE WIDGET STACK! This is unique from //joypad->index and has nothing to do with device number. int index; //how many times one of the components has flashed on minus how many //times one of the components has flashed off. This is how we know when //none of the components are left lit. int flashcount; //the parts of the dialog QGridLayout *LMain; AxisWidget **Axes; ButtonWidget **Buttons; QPushButton *BClear, *BAll; //the quickset window, when we create it QuickSet* quickset; }; #endif ./qjoypad-4.1.0/src/button_edit.h0000644000175000001440000000075511235433371015466 0ustar johnusers#ifndef BUTTON_EDIT_H #define BUTTON_EDIT_H #include #include //we need to edit a Button #include "button.h" //to get a new key for the button. #include "keycode.h" class ButtonEdit : public QDialog { Q_OBJECT public: ButtonEdit(Button* butt); void show(); protected slots: void accept(); protected: Button *button; KeyButton *BKKey; QPushButton *BKey, *BMouse, *BOkay, *BCancel; QPushButton **BMKey; QCheckBox *CSticky, *CRapid; }; #endif ./qjoypad-4.1.0/src/quickset.h0000644000175000001440000000136711235433371014776 0ustar johnusers#ifndef QUICKSET_H #define QUICKSET_H //for building the dialog #include #include #include #include //to request new keycodes //#include "keycode.h" //to actually set the joypad #include "joypad.h" //because of some circularity issues ;) class JoyPad; //a dialog to quickly set a key to a button class QuickSet : public QDialog { public: QuickSet(JoyPad* jp); //this needs to see js_events so it can capture them directly void jsevent( js_event msg ); private: //the joypad that is being configured JoyPad* joypad; //when a joystick event has trigered QuickSet, setting is true and it //is waiting for a keypress or mouse click. Otherwise, setting is false. bool setting; }; #endif ./qjoypad-4.1.0/src/joypad.cpp0000644000175000001440000002146411332164615014767 0ustar johnusers#include "unistd.h" #include "joypad.h" #include #include #include #include #include JoyPad::JoyPad( int i, int dev ) { debug_mesg("Constructing the joypad device with index %d and fd %d\n", i, dev); //remember the index, index = i; //load data from the joystick device, if available. joydevFileHandle = NULL; if(dev > 0) { debug_mesg("Valid file handle, setting up handlers and reading axis configs...\n"); resetToDev(dev); debug_mesg("done resetting and setting up device index %d\n", i); char id[200]; ioctl(joydev, JSIOCGNAME(199), id); deviceId = id; } else { debug_mesg("This joypad does not have a valid file handle, not setting up event listeners\n"); } //there is no JoyPadWidget yet. jpw = NULL; debug_mesg("Done constructing the joypad device %d\n", i); } void JoyPad::resetToDev(int dev ) { debug_mesg("resetting to dev\n"); //remember the device file descriptor joydev = dev; //read in the number of axes / buttons axes = 0; ioctl (joydev, JSIOCGAXES, &axes); buttons = 0; ioctl (joydev, JSIOCGBUTTONS, &buttons); //make sure that we have the axes we need. //if one that we need doesn't yet exist, add it in. //Note: if the current layout has a key assigned to an axis that did not //have a real joystick axis mapped to it, and this function suddenly brings //that axis into use, the key assignment will not be lost because the axis //will already exist and no new axis will be created. for (int i = 0; i < axes; i++) { if (Axes[i] == 0) Axes.insert(i, new Axis( i, this )); } for (int i = 0; i < buttons; i++) { if (Buttons[i] == 0) Buttons.insert(i, new Button( i, this )); } struct pollfd read_struct; read_struct.fd = joydev; read_struct.events = POLLIN; char buf[10]; while(poll(&read_struct, 1, 5)!=0) { debug_mesg("reading junk data\n"); read(joydev, buf, 10); } setupJoyDeviceListener(dev); debug_mesg("done resetting to dev\n"); } void JoyPad::setupJoyDeviceListener(int dev) { debug_mesg("Setting up joyDeviceListeners\n"); joydevFileHandle = new QSocketNotifier(dev, QSocketNotifier::Read, this); connect(joydevFileHandle, SIGNAL(activated(int)), this, SLOT(handleJoyEvents(int))); joydevFileException = new QSocketNotifier(dev, QSocketNotifier::Exception, this); connect(joydevFileException, SIGNAL(activated(int)), this, SLOT(errorRead(int))); debug_mesg("Done setting up joyDeviceListeners\n"); } void JoyPad::toDefault() { //to reset the whole, reset all the parts. do { QHashIterator it( Axes ); while (it.hasNext()) { it.next(); it.value()->toDefault(); } } while (0); do { QHashIterator it( Buttons ); while (it.hasNext()) { it.next(); it.value()->toDefault(); } } while (0); } bool JoyPad::isDefault() { //if any of the parts are not at default, then the whole isn't either. do { QHashIterator it( Axes ); while (it.hasNext()) { it.next(); if (!it.value()->isDefault()) return false; } } while (0); do { QHashIterator it( Buttons ); while (it.hasNext()) { it.next(); if (!it.value()->isDefault()) return false; } } while (0); return true; } bool JoyPad::readConfig( QTextStream* stream ) { toDefault(); QString word; QChar dump; int num; *stream >> word; while (word != NULL && word != "}") { word = word.toLower(); if (word == "button") { *stream >> num; if (num > 0) { *stream >> dump; if (dump != ':') { error("Layout file error", "Expected ':', found '" + QString(dump) + "'."); return false; } if (Buttons[num-1] == 0) { Buttons.insert(num-1,new Button(num-1)); } if (!Buttons[num-1]->read( stream )) { error("Layout file error", "Error reading Button " + QString::number(num)); return false; } } else { stream->readLine(); } } else if (word == "axis") { *stream >> num; if (num > 0) { *stream >> dump; if (dump != ':') { error("Layout file error", "Expected ':', found '" + QString(dump) + "'."); return false; } if (Axes[num-1] == 0) { Axes.insert(num-1,new Axis(num-1)); } if (!Axes[num-1]->read(stream)) { error("Layout file error", "Error reading Axis " + QString::number(num)); return false; } } } else { error( "Layout file error", "Error while reading layout. Unrecognized word: " + word ); return false; } *stream >> word; } return true; } //only actually writes something if this JoyPad is NON DEFAULT. void JoyPad::write( QTextStream* stream ) { int i = 0; //use to test if this is default or not. QString result; QTextStream* s = new QTextStream(&result, QIODevice::WriteOnly); *s << getName() << " {\n"; do { QHashIterator it( Axes ); while (it.hasNext()) { it.next(); if (!it.value()->isDefault()) { it.value()->write( s ); ++i; } } } while (0); QHashIterator it( Buttons ); while (it.hasNext()) { it.next(); if (!it.value()->isDefault()) { it.value()->write( s ); ++i; } } if (i > 0) { *stream << result << "}\n\n"; } } void JoyPad::release() { do { QHashIterator it( Axes ); while (it.hasNext()) { it.next(); it.value()->release(); } } while (0); do { QHashIterator it( Buttons ); while (it.hasNext()) { it.next(); it.value()->release(); } } while (0); } void JoyPad::jsevent( js_event msg ) { //if there is a JoyPadWidget around, ie, if the joypad is being edited if (jpw != NULL && hasFocus) { //tell the dialog there was an event. It will use this to flash //the appropriate button, if necesary. jpw->jsevent(msg); return; } //if the dialog is open, stop here. We don't want to signal ourselves with //the input we generate. if (qApp->activeWindow() != 0 && qApp->activeModalWidget() != 0) return; //otherwise, lets create us a fake event! Pass on the event to whichever //Button or Axis was pressed and let them decide what to do with it. if (msg.type & JS_EVENT_AXIS) { debug_mesg("DEBUG: passing on an axis event\n"); debug_mesg("DEBUG: %d %d\n", msg.number, msg.value); Axes[msg.number]->jsevent(msg.value); } else { debug_mesg("DEBUG: passing on a button event\n"); debug_mesg("DEBUG: %d %d\n", msg.number, msg.value); Buttons[msg.number]->jsevent(msg.value); } } JoyPadWidget* JoyPad::widget( QWidget* parent, int i) { //create the widget and remember it. jpw = new JoyPadWidget(this, i, parent); return jpw; } void JoyPad::handleJoyEvents(int fd) { js_event msg; int len; len = read( joydev, &msg, sizeof(js_event)); //if there was a real event waiting, if (len == (int) sizeof(js_event)) { //pass that event on to the joypad! jsevent(msg); } } void JoyPad::releaseWidget() { //this is how we know that there is no longer a JoyPadWidget around. jpw = NULL; } void JoyPad::unsetDev() { close(joydev); joydev = -1; if(joydevFileHandle != NULL) { delete joydevFileHandle; } } void JoyPad::errorRead(int fd) { debug_mesg("There was an error reading off of the device with fd %d, disabling\n", fd); joydevFileHandle->blockSignals(true); joydevFileHandle->setEnabled(false); close(joydev); if(disconnect(joydevFileHandle , 0, 0, 0)) { joydevFileHandle->deleteLater(); joydevFileHandle = NULL; } if(disconnect(joydevFileException, 0, 0, 0)) { joydevFileException->setEnabled(false); joydevFileException->deleteLater(); joydevFileException = NULL; } joydev = -1; debug_mesg("Done disabling device with fd %d\n", fd); } void JoyPad::focusChange(bool focusState) { hasFocus = !focusState; } ./qjoypad-4.1.0/src/axisw.cpp0000644000175000001440000000155511235433371014633 0ustar johnusers#include "axisw.h" AxisWidget::AxisWidget( Axis* a, QWidget* parent ) : FlashButton( "",parent) { axis = a; ae = NULL; update(); on = false; } void AxisWidget::jsevent( int val ) { bool newOn = !axis->inDeadZone(val); if (on != newOn) { on = newOn; flash(); } if (ae != NULL) ae->setState(val); } void AxisWidget::update() { setText( axis->status()); } void AxisWidget::mouseReleaseEvent( QMouseEvent* e ) { //create the edit dialog, ae = new AxisEdit(axis); //get its input ae->exec(); //now that it's done, destroy it! delete ae; //and remember that it's gone. ae = NULL; update(); //release the button. Waiting to do this until the very end has the nice //effect of keeping the button depressed while the dialog is shown. FlashButton::mouseReleaseEvent( e ); } ./qjoypad-4.1.0/src/qjoypad.pro0000644000175000001440000000337711311571457015174 0ustar johnusers######################################## # # # QMake project file for QJoyPad # # # ######################################## ##### Setup Targets ##### icons.path = $$INSTALL_PREFIX/share/pixmaps/qjoypad icons.conf_path = $$PREFIX/share/pixmaps/qjoypad icons.extra = cp ../icons/* $${icons.path}; cd $${icons.path}; ln -sf gamepad4-24x24.png icon24.png; ln -sf gamepad3-64x64.png icon64.png; chmod -R a+r $${icons.path} doc.path = $$INSTALL_PREFIX/share/doc/qjoypad4 doc.extra = cp ../README.txt ../LICENSE.txt $${doc.path} target.path = $$INSTALL_PREFIX/bin ##### Setup Compile ##### DEFINES += DEVDIR='\\\"$$DEVDIR\\\"' DEFINES += ICON24='\\\"$${icons.conf_path}/icon24.png\\\"' DEFINES += ICON64='\\\"$${icons.conf_path}/icon64.png\\\"' TEMPLATE = app INCLUDEPATH += . QMAKE_LIBS += -lXtst # Input HEADERS += axis.h \ axis_edit.h \ axisw.h \ button.h \ button_edit.h \ buttonw.h \ constant.h \ device.h \ error.h \ event.h \ flash.h \ icon.h \ joypad.h \ joypadw.h \ joyslider.h \ keycode.h \ layout.h \ getkey.h \ layout_edit.h \ quickset.h SOURCES += axis.cpp \ axis_edit.cpp \ axisw.cpp \ button.cpp \ button_edit.cpp \ buttonw.cpp \ event.cpp \ flash.cpp \ icon.cpp \ joypad.cpp \ joypadw.cpp \ joyslider.cpp \ keycode.cpp \ layout.cpp \ layout_edit.cpp \ main.cpp \ quickset.cpp \ getkey.cpp ##### Install ##### INSTALLS += target icons doc ./qjoypad-4.1.0/src/main.cpp0000644000175000001440000001637611244325206014430 0ustar johnusers#define MAIN //to create a qapplication #include //for ouput when there is no GUI going #include #include //to create and handle signals for various events #include //to load layouts #include "layout.h" //to give event.h the current X11 display #include "event.h" //to update the joystick device list #include "device.h" //to produce errors! #include "error.h" #include #include #include #include //for making universally available variables extern Display* display; //from event.h QHash available; //to device.h QHash joypads; //to device.h //variables needed in various functions in this file LayoutManager* lm; QString devdir = DEVDIR; //signal handler for SIGIO //SIGIO means that a new layout should be loaded. It is saved in // ~/.qjoypad/layout, where the last used layout is put. void catchSIGIO( int sig ) { lm->load(); //remember to catch this signal again next time. signal( sig, catchSIGIO ); } //signal handler for SIGUSR1 //SIGUSR1 means that we should update the available joystick device list. void catchSIGUSR1( int sig ) { //buildJoyDevices(); lm->updateJoyDevs(); //remember to catch this signal again next time. signal( sig, catchSIGUSR1 ); } /* A new feature to add? We'll see. void catchSIGUSR2( int sig ) { lm->trayClick(); signal( sig, catchSIGUSR2 ); } */ int main( int argc, char **argv ) { //create a new event loop. This will be captured by the QApplication //when it gets created QApplication a( argc, argv ); a.setQuitOnLastWindowClosed(false); //where to look for settings. If it does not exist, it will be created QDir dir(settingsDir); //if there is no new directory and we can't make it, complain if (!dir.exists() && !dir.mkdir(settingsDir)) { printf("Couldn't create the QJoyPad save directory (%s)!", settingsDir.toStdString().c_str()); return 1; } //start out with no special layout. QString layout = ""; //by default, we use a tray icon bool useTrayIcon = true; //this execution wasn't made to update the joystick device list. bool update = false; bool forceTrayIcon = false; //parse command-line options for (int i = 1; i < a.argc(); i++) { //if a device directory was specified, if (QRegExp("-{1,2}device").exactMatch(a.argv()[i])) { ++i; if (i < a.argc()) { if (QFile::exists(a.argv()[i])) { devdir = a.argv()[i]; } else { error("Command Line Argument Problems", "No such directory: " + QString(a.argv()[i])); continue; } } } //if no-tray mode was requested, else if (QRegExp("-{1,2}notray").exactMatch(a.argv()[i])) { useTrayIcon = false; } //if this execution is just meant to update the joystick devices, else if (QRegExp("-{1,2}update").exactMatch(a.argv()[i])) { update = true; } //if help was requested, else if (QRegExp("-{1,2}h(elp)?").exactMatch(a.argv()[i])) { printf(NAME"\nUsage: qjoypad [--device \"/device/path\"] [--notray] [\"layout name\"]\n\nOptions:\n --device path Look for joystick devices in \"path\". This should\n be something like \"/dev/input\" if your game\n devices are in /dev/input/js0, /dev/input/js1, etc.\n --notray Do not use a system tray icon. This is useful for\n window managers that don't support this feature.\n --update Force a running instance of QJoyPad to update its\n list of devices and layouts.\n \"layout name\" Load the given layout in an already running\n instance of QJoyPad, or start QJoyPad using the\n given layout.\n"); return 1; } else if(QRegExp("--force-tray").exactMatch(a.argv()[i])) { useTrayIcon = true; forceTrayIcon = true; } //in all other cases, an argument is assumed to be a layout name. //note: only the last layout name given will be used. else layout = a.argv()[i]; } //if the user specified a layout to use, if (layout != "") { //then we try to store that layout in the last-used layout spot, to be //loaded by default. QFile file( settingsDir + "layout"); if (file.open(QIODevice::WriteOnly)) { QTextStream stream( &file ); stream << layout; file.close(); } } //create a pid lock file. QFile pidFile( "/tmp/qjoypad.pid" ); //if that file already exists, then qjoypad is already running! if (pidFile.exists()) { int pid; if (pidFile.open( QIODevice::ReadOnly )); { //try to get that pid... QTextStream( &pidFile ) >> pid; pidFile.close(); //if we can signal the pid (ie, if the process is active) if (kill(pid,0) == 0) { //then prevent two instances from running at once. //however, if we are setting the layout or updating the device //list, this is not an error and we shouldn't make one! if (layout == "" && update == false) error("Instance Error","There is already a running instance of QJoyPad; please close\nthe old instance before starting a new one."); else { //if one of these is the case, send the approrpriate signal! if (update == true) kill(pid,SIGUSR1); if (layout != "") kill(pid,SIGIO); } //and quit. We don't need two instances. return 0; } } } //now we can try to create and write our pid to the lock file. if (pidFile.open( QIODevice::WriteOnly )) { QTextStream( &pidFile ) << getpid(); pidFile.close(); } //prepare the signal handlers signal( SIGIO, catchSIGIO ); signal( SIGUSR1, catchSIGUSR1 ); // signal( SIGUSR2, catchSIGUSR2 ); if(forceTrayIcon) { int sleepCounter = 0; while(!QSystemTrayIcon::isSystemTrayAvailable()) { sleep(1); sleepCounter++; if(sleepCounter > 20) { printf("Error, we've waited more than 20 seconds, your sys tray probably isn't loading\n"); exit(1); } } } //capture the current display for event.h display = QX11Info::display(); //create a new LayoutManager with a tray icon / floating icon, depending //on the user's request lm = new LayoutManager(useTrayIcon); //build the joystick device list for the first time, //buildJoyDevices(); lm->updateJoyDevs(); //load the last used layout (Or the one given as a command-line argument) lm->load(); //and run the program! int result = a.exec(); //when everything is done, save the current layout for next time... lm->saveDefault(); //remove the lock file... pidFile.remove(); //and terminate! return result; } ./qjoypad-4.1.0/src/icon.h0000644000175000001440000000074111235433371014071 0ustar johnusers#ifndef JOY_ICON_H #define JOY_ICON_H #include #include #include #include #include #include "constant.h" class FloatingIcon : public QDialog { Q_OBJECT public: FloatingIcon( const QPixmap &icon, QMenu *popup = 0, QWidget *parent = 0, const char *name = 0); signals: void closed(); void clicked(); protected: void mousePressEvent( QMouseEvent* e ); void closeEvent( QCloseEvent* e ); QMenu* pop; }; #endif ./qjoypad-4.1.0/src/joyslider.cpp0000644000175000001440000001702311235433371015501 0ustar johnusers#include "joyslider.h" //Added by qt3to4: JoySlider::JoySlider( int dz, int xz, int val, QWidget* parent ) :QWidget( parent ) { //initialize :) JoyVal = val; DeadZone = dz; XZone = xz; setMinimumHeight(20); } void JoySlider::setValue( int newval ) { //adjust the new position based on the throttle settings if (throttle == 0) JoyVal = newval; else if (throttle < 0) JoyVal = (newval + JOYMIN) / 2; else JoyVal = (newval + JOYMAX) / 2; //then redraw! update(); } void JoySlider::setThrottle( int newval ) { //change throttle settings. This WILL quite likely cause minor issues with //status if the axis is not currently at zero, but these will be corrected //as soon as the axis moves again. throttle = newval; update(); } int JoySlider::pointFor( int value, bool negative ) { //complicated... this just finds the pixel the given value should be. if (throttle == 0) { int result = ((boxwidth - 4) * value) / JOYMAX; if (negative) result = lboxstart + boxwidth - 2 - result; else result += rboxstart + 2; return result; } else { int result = ((twidth - 4) * value) / JOYMAX; if (negative) result = lboxstart + twidth - 2 - result; else result += lboxstart + 2; return result; } } int JoySlider::valueFrom( int point ) { //the inverse of above :) if (throttle == 0) { if (point <= lboxstart) return JOYMAX; if (point >= lboxend - 2 && point <= rboxstart + 2) return 0; if (point >= rboxend - 2) return JOYMAX; if (point < lboxend - 2) return ((lboxend - point) * JOYMAX) / boxwidth; if (point > rboxstart) return ((point - rboxstart) * JOYMAX) / boxwidth; else return 0; } else if (throttle > 0) { if (point <= lboxstart) return 0; else if (point >= tend) return JOYMAX; else return ((point - lboxstart) * JOYMAX) / twidth; } else { if (point <= lboxstart - 2) return JOYMAX; else if (point >= tend) return 0; else return ((tend - point) * JOYMAX) / twidth; } } void JoySlider::resizeEvent( QResizeEvent* ) { //when we resize, we need to recalculate a bunch of measurements. boxwidth = (this->width() - 6) / 2 - 1; twidth = this->width() - 4; boxheight = this->height() - 4; lboxstart = 1; lboxend = lboxstart + boxwidth - 1; tend = lboxstart + twidth - 1; rboxstart = lboxend + 5; rboxend = rboxstart + boxwidth - 1; } void JoySlider::drawBox( int x, int width ) { //draws a nice, pretty, 3d-styled box. that takes up the full height of the //widget but is defined by x-coordinate and width QPainter paint( this ); paint.setPen( (isEnabled())?Qt::white:palette().background().color() ); paint.setBrush( (isEnabled())?Qt::white:palette().background() ); paint.drawRect( x, 1, width, boxheight); paint.setPen( palette().dark().color() ); paint.drawLine( x, 1 + boxheight, x, 1 ); paint.drawLine( x, 1, x + width - 1, 1); paint.setPen( palette().shadow().color() ); paint.drawLine( x + 1, 1 + boxheight - 1, x + 1, 2); paint.drawLine( x + 1, 2, x + width - 2, 2); paint.setPen( palette().light().color() ); paint.drawLine( x + 2, 1 + boxheight - 1, x + width - 1, 1 + boxheight - 1); paint.drawLine( x + width - 1, 1 + boxheight - 1, x + width - 1, 2); paint.setPen( palette().midlight().color() ); paint.drawLine( x + 1, 1 + boxheight, x + width, 1 + boxheight ); paint.drawLine( x + width, 1 + boxheight, x + width, 1 ); } void JoySlider::paintEvent( QPaintEvent* ) { //when we need to redraw, //start by making our boxes if (throttle == 0) { drawBox( lboxstart, boxwidth ); drawBox( rboxstart, boxwidth ); } //or box, if we are in throttle mode. else { drawBox( lboxstart, twidth ); } //if this is disabled, that's enough of that. if (!isEnabled()) return; //now we need to draw. QPainter paint( this ); //prepare to draw a bar of the appropriate color QColor bar; if (abs(JoyVal) < DeadZone) bar = Qt::gray; else if (abs(JoyVal) < XZone) bar = Qt::blue; else bar = Qt::red; paint.setPen( bar ); paint.setBrush( bar ); //find out the dimensions of the bar, then draw it int width = (throttle == 0)?boxwidth:twidth; int barlen = abs(((width - 4) * JoyVal) / JOYMAX); if (JoyVal > 0) paint.drawRect( ((throttle == 0)?rboxstart:lboxstart) + 2, 3, barlen, boxheight - 3 ); else if (JoyVal < 0) paint.drawRect( lboxstart + width - 2 - barlen, 3, barlen, boxheight - 3 ); //and now draw the tabs! We only need one set if we're doing a throttle mode //but we need two if we're not. However, it's important to draw the right //set of tabs depending on the mode! Negative throttle gets negative tabs. int point; QPolygon shape; paint.setPen( Qt::black ); paint.setBrush( Qt::blue ); if (throttle >= 0) { point = pointFor(DeadZone, false); shape.putPoints(0,5, point, boxheight - 4, point + 3, boxheight - 1, point + 3, boxheight + 2, point - 3, boxheight + 2, point - 3, boxheight - 1); paint.drawPolygon(shape); } if (throttle <= 0) { point = pointFor(DeadZone, true); shape.putPoints(0,5, point, boxheight - 4, point + 3, boxheight - 1, point + 3, boxheight + 2, point - 3, boxheight + 2, point - 3, boxheight - 1); paint.drawPolygon(shape); } paint.setBrush( Qt::red ); if (throttle >= 0) { point = pointFor(XZone, false); shape.putPoints(0,5, point, boxheight - 4, point + 3, boxheight - 1, point + 3, boxheight + 2, point - 3, boxheight + 2, point - 3, boxheight - 1); paint.drawPolygon(shape); } if (throttle <= 0) { point = pointFor(XZone, true); shape.putPoints(0,5, point, boxheight - 4, point + 3, boxheight - 1, point + 3, boxheight + 2, point - 3, boxheight + 2, point - 3, boxheight - 1); paint.drawPolygon(shape); } } void JoySlider::mousePressEvent( QMouseEvent* e ) { //store the x coordinate. int xpt = e->x(); int result = 0; //see if this happened near one of the tabs. If so, start dragging that tab. if (throttle <= 0 && abs( xpt - pointFor( XZone, true )) < 5) result = DRAG_XZ; else if (throttle <= 0 && abs( xpt - pointFor( DeadZone, true )) < 5) result = DRAG_DZ; else if (throttle >= 0 && abs( xpt - pointFor( XZone, false )) < 5) result = DRAG_XZ; else if (throttle >= 0 && abs( xpt - pointFor( DeadZone, false )) < 5) result = DRAG_DZ; dragging = result; }; void JoySlider::mouseReleaseEvent( QMouseEvent* ) { //when the mouse releases, all dragging stops. dragging = 0; } void JoySlider::mouseMoveEvent( QMouseEvent* e ) { //get the x coordinate int xpt = e->x(); //if we're dragging, move the appropriate tab! if (dragging == DRAG_XZ) { XZone = valueFrom( xpt ); } else if (dragging == DRAG_DZ) { DeadZone = valueFrom( xpt ); } else return; //if we moved a tab, redraw! update(); } ./qjoypad-4.1.0/src/getkey.h0000644000175000001440000000126011161531005014415 0ustar johnusers#include #include #include #include #include "constant.h" #include //The KeySym for "x" #define XK_x 0x078 extern Display *display; //a keycode dialog box class GetKey : public QDialog { Q_OBJECT public: GetKey( QString button, bool m = false ); protected: //to filter through every event this thing sees, before QT does. bool x11Event( XEvent* e ); //to avoid focus issues, there is only the dialog widget, all the //rest is painted on. So, I need to know when to repaint. void paintEvent ( QPaintEvent * ); private: //the dialog's message QString Text; //does this dialog accept mouse clicks? bool mouse; }; ./qjoypad-4.1.0/src/icon.cpp0000644000175000001440000000145311235433371014425 0ustar johnusers#include "icon.h" FloatingIcon::FloatingIcon( const QPixmap &icon, QMenu *popup, QWidget *parent, const char *name) : QDialog( parent ) { this->setObjectName(name); setWindowTitle(NAME); QPalette palette; palette.setBrush(backgroundRole(), QBrush(icon)); setPalette(palette); //setPaletteBackgroundPixmap(icon); pop = popup; setFixedSize(64,64); } void FloatingIcon::mousePressEvent( QMouseEvent* e ) { //if it was the right mouse button, if (e->button() == Qt::RightButton) { //bring up the popup menu. pop->popup( e->globalPos() ); e->accept(); } else { //otherwise, treat it as a regular click. emit clicked(); } } void FloatingIcon::closeEvent( QCloseEvent* e ) { emit closed(); e->accept(); } ./qjoypad-4.1.0/src/getkey.cpp0000644000175000001440000000365311161531005014760 0ustar johnusers#include "getkey.h" GetKey::GetKey( QString button, bool m ) :QDialog( 0 ) { //prepare the dialog mouse = m; setWindowTitle( "Choose a key" ); setWindowIcon(QIcon(ICON24)); //I'd use a QLabel, but that steals x11Events! //So, I'll draw the text directly. That means //I need to resolve the size of the dialog by hand: Text = "Choose a new key "; if (mouse) Text += "or mouse button "; Text += "for " + button; QRect rect = fontMetrics().boundingRect( Text ); //I calculate the size based on the first line of text, which is longer. //The fontMetrics function is dumb and would treat the string with a //newline in it as a continues flow of characters if I did the whole string //at once. Text += "\n(Ctrl-X for no key)"; //now I add 20 pixels of padding and double the height to make room for //two lines. setFixedSize( QSize( rect.width() + 20, rect.height()*2 + 20 ) ); } bool GetKey::x11Event( XEvent* e ) { //keep Qt from closing the dialog upon seeing Esc pressed. if (e->type == KeyPress) return true; //On a key press, return the key and quit //Ctrl+X == [No Key] if (e->type == KeyRelease) { if (XKeycodeToKeysym(display,e->xkey.keycode,0) == XK_x ) { if (e->xkey.state & ControlMask) done( 0 ); else done( e->xkey.keycode ); } else done( e->xkey.keycode ); return true; } //if we're accepting mouse clicks and a mouse button was clicked... if (mouse && e->type == ButtonRelease) { done ( e->xbutton.button + MOUSE_OFFSET); return true; } //any other events we will pass on to the dialog. This allows for closing //the window and easy redrawing :) return false; } void GetKey::paintEvent ( QPaintEvent * ) { //whenever we need to repaint, draw in our text. QPainter paint( this ); paint.drawText( rect(), Qt::AlignCenter, Text ); } ./qjoypad-4.1.0/src/configure0000777000175000001440000000000011332170104016045 2configustar johnusers./qjoypad-4.1.0/src/keycode.h0000644000175000001440000000134311235433371014563 0ustar johnusers#ifndef KEYCODE_H #define KEYCODE_H //To create the "press a key" dialog: #include #include #include #include #include "constant.h" //Produce a string for any keycode const QString ktos( int keycode ); //The X11 display, taken from main.cpp extern Display* display; //a button that requests a keycode from the user when clicked. class KeyButton : public QPushButton { Q_OBJECT public: KeyButton(QString name, int val, QWidget* parent, bool m = false, bool nowMouse = false); int getValue() {return value;}; int choseMouse() {return mouseClicked;}; protected slots: void onClick(); private: QString buttonname; int value; bool mouse; bool mouseClicked; }; #endif ./qjoypad-4.1.0/src/event.cpp0000644000175000001440000000120511235433371014611 0ustar johnusers#include "event.h" //this should be initialized by main.cpp as soon as the program starts. Display* display; //actually creates an XWindows event :) void sendevent( xevent e ) { if (e.value1 == 0 && e.value2 == 0) return; if (e.type == WARP) { XTestFakeRelativeMotionEvent(display, e.value1, e.value2, 0); } else { if (e.type == KREL || e.type == KPRESS) { XTestFakeKeyEvent(display, e.value1, (e.type == KPRESS), 0); } else if (e.type == BREL || e.type == BPRESS) { XTestFakeButtonEvent(display, e.value1, (e.type == BPRESS), 0); } } XFlush(display); } ./qjoypad-4.1.0/src/constant.h0000644000175000001440000000125211332164615014770 0ustar johnusers#ifndef JCONST_H #define JCONST_H //How many cycles there are per click. #define FREQ 10 //How many milliseconds per cycle. #define MSEC 5 //events will be generated every FREQ * MSEC milliseconds. The duration of the //event can be anywhere between 0 * MSEC and FREQ * MSEC. This means there will //be FREQ + 1 levels of gradation. //maximum range of values from joystick driver #define JOYMAX 32767 #define JOYMIN -32767 //maximum number of defined keys #define MAXKEY 122 //fastest the mouse can go. Completely arbitrary. #define MAXMOUSESPEED 5000 #define SENSITIVITY_MIN 1e-8F #define SENSITIVITY_MAX 1e+8F #define NAME "QJoyPad 4.1" #define MOUSE_OFFSET 400 #endif ./qjoypad-4.1.0/src/quickset.cpp0000644000175000001440000000356511235431551015331 0ustar johnusers#include "quickset.h" #include "getkey.h" //build the dialog QuickSet::QuickSet( JoyPad* jp) : QDialog() { setting = false; joypad = jp; setWindowTitle("Set " + jp->getName()); QVBoxLayout* LMain = new QVBoxLayout(this); LMain->setMargin(5); LMain->setSpacing(5); //LMain->setAutoAdd(true); QLabel *temp = new QLabel("Press any button or axis and\nyou will be prompted for a key.",this); LMain->addWidget(temp); QPushButton* button = new QPushButton("Done",this); LMain->addWidget(button); connect( button, SIGNAL(clicked()), this, SLOT(accept())); } void QuickSet::jsevent( js_event msg ) { //ignore any joystick events if we're waiting for a keypress if (setting) return; //if a button was pressed on the joystick if (msg.type == JS_EVENT_BUTTON) { //capture that button. Button* button = joypad->Buttons[msg.number]; //go into setting mode and request a key/mousebutton setting = true; int code = GetKey(button->getName(), true).exec(); setting = false; //if a mouse button was used, if (code > MOUSE_OFFSET) //then tell it to the Button a mouse button button->setKey(true, code - MOUSE_OFFSET); else //otherwise, tell it to use a keycode. button->setKey(false, code); } else { //require a signal strength of at least 5000 to consider an axis moved. if (abs(msg.value) < 5000) return; //capture the axis that moved Axis* axis = joypad->Axes[msg.number]; //grab a keycode for that axis and that direction setting = true; int code = GetKey(axis->getName() + ", " + QString((msg.value > 0)?"positive":"negative"), false).exec(); setting = false; //assign the key to the axis. axis->setKey((msg.value > 0),code); } } ./qjoypad-4.1.0/src/config0000755000175000001440000000756711315257566014211 0ustar johnusers#!/bin/bash if( ! pkg-config --exists xtst ); then echo "Error: you will need libxtst to compile this program"; exit 1; fi; if( ! pkg-config --atleast-version=4.2 QtCore ); then echo "Error: you need at least Qt version 4.2 to use this program"; exit 1; fi; if ( ! qmake ); then echo "Error: you need qmake!"; exit 1; fi; if( ! ( qmake --version | grep -qi 'Qt version 4' ) ); then echo "WARNING: Falling back on qmake-qt4; this may or may not work"; echo "WARNING: if it fails please see the compilation instructions on qjoypad.sourceforge.net"; QMAKE_EXEC="qmake-qt4"; else QMAKE_EXEC="qmake"; fi; devdir="/dev/input" prefix="/usr/local" installdir=""; plain_keys="" debug_option=""; build_mode="release"; for arg in $* do case $arg in --help) echo " Usage: ./config [--devdir=\"dir\"] [--prefix=\"prefix\"] [--help] Options: --devdir=DIR Set the path where QJoyPad will look for your joystick devices to be DIR. If your devices are /dev/js0, /dev/js1, etc., this should be just \"/dev\". By default, this is /dev/input. --prefix=DIR Set the path where QJoyPad and its components will be installed. By default, this is /usr/local. --plain_keys Force QJoyPad to use standard XWindows keynames without filtering them for appearance. This will make displays less attractive and readable, but will save processor power and ensure that you see the right names for keys you press. --install-dir=DIR Sets an optional installation path that will be prepended to prefix during installation. --debug Causes Qjoypad to spit out debugging messages (debug messages will be compiled in; to get rid of the messages you'll have to recompile). --qmake4bin=EXEC Overrides the qmake executable used by this script. EXEC may be relative to PATH or an absolute pathname. --help Show this message. "; exit 0;; --devdir=*) devdir=${arg##*=} if [[ ! -d $devdir ]] then echo "Invalid device directory given: $devdir" exit 1 fi ;; --prefix=*) prefix=${arg##*=} if [[ ! -d $prefix ]] then echo "Invalid prefix directory given: $prefix" exit 1 fi;; --install-dir=*) installdir=${arg##*=} if [[ ! -d $installdir ]] then echo "invalid destination directory given: $installdir"; exit 1 fi;; --plain_keys) plain_keys="PLAIN_KEYS";; --debug) debug_option+="_DEBUG"; build_mode="debug";; --qmake4bin=*) QMAKE_EXEC=${arg##*=};; *) echo "Unrecognized argument: \"$arg\". Try ./config --help for help." esac done if ( ! $QMAKE_EXEC -makefile DEVDIR=$devdir PREFIX=$prefix \ "DEFINES += $plain_keys $debug_option" \ INSTALL_PREFIX=${installdir}/${prefix}/ \ "CONFIG += $build_mode" \ qjoypad.pro ); then echo "Config failed. If you overrode the qmake exec, make sure it is valid! Otherwise, email virtuoussin13@users.sourceforge.net for help"; exit 1; fi echo " Configuring QJoyPad installation... ------------------------------------------------------------ Device directory: $devdir -- Devices will be looked for in: $devdir/js0 $devdir/js1 etc. Prefix directory: $prefix -- Files to be installed in: $prefix/bin $prefix/doc $prefix/share/pixmaps" if [[ -n $plain_keys ]]; then echo " -- Using regular XWindows key names."; fi echo " --------------------------------------------------------- If these settings are okay, go ahead and run 'make' and then 'make install'. To make changes, run ./config --help for details. " ./qjoypad-4.1.0/src/axisw.h0000644000175000001440000000145411235433371014276 0ustar johnusers#ifndef AXIS_WIDGET_H #define AXIS_WIDGET_H #include //so we can interact with the axis this refers to #include "axis.h" //for the FlashButton widget #include "flash.h" //so we can edit this axis when the user clicks the button #include "axis_edit.h" class AxisWidget : public FlashButton { public: AxisWidget( Axis* a, QWidget* parent ); //this is notified on a jsevent so it can flash if necesary. void jsevent( int val ); //change the text on this button to reflect the axis' current state. void update(); private: //to deal with clicking (by creating an AxisEdit dialog) void mouseReleaseEvent( QMouseEvent* e ); //is this button currently blue? bool on; //the axis this refers to Axis* axis; //the edit dialog that we pop up on request. AxisEdit* ae; }; #endif ./qjoypad-4.1.0/src/axis.h0000644000175000001440000000600711332164615014106 0ustar johnusers#ifndef AXIS_H #define AXIS_H //abs() #include #include #include #include #include #include #include "constant.h" #include "error.h" //default and arbitrary values for dZone and xZone #define DZONE 3000 #define XZONE 30000 //each axis can create a key press or move the mouse in one of four directions. enum AxisMode {keybd, mousepv, mousenv, mouseph, mousenh}; enum TransferCurve {linear, quadratic, cubic, quadratic_extreme, power_function}; //represents one joystick axis class Axis : public QObject { Q_OBJECT //so AxisEdit can manipulate fields directly. friend class AxisEdit; public: Axis( int i, QObject *parent = 0 ); ~Axis(); //read axis settings from a stream bool read( QTextStream* stream ); //write axis settings to a stream void write( QTextStream* stream ); //releases any pushed buttons and returns to a neutral state void release(); //pass a message from the joystick device to this axis object void jsevent( int value ); //revert to default settings void toDefault(); //True iff currently at defaults bool isDefault(); QString getName() { return "Axis " + QString::number(index+1);}; //true iff the given value is in the dead zone for this axis. bool inDeadZone( int val ); //a descriptive string used as a label for the button representing this axis QString status(); //set the key code for this axis. Used by quickset. void setKey(bool positive, int value); //happens every MSEC milliseconds (constant.h) //uses tick to decide if key events should be generated void timerTick( int tick ); //recalculates the gradient curve. This should be run every time //maxSpeed, xZone, or dZone are changed. void adjustGradient(); protected: int tick; //This axis is logically depressed (positive or negative) //if the axis is gradient, this is true even if it is not //currently generating a keypress at the instant. bool isOn; //which joystick this actually is int index; //actually sends key events. Press is true iff the key //is to be depressed as opposed to released. virtual void move( bool press ); //is a key currently depressed? bool isDown; //variables for calculating quadratic used for gradient mouse axes float inverseRange; //actual axis settings: bool gradient; int maxSpeed; //0..MAXMOUSESPEED unsigned int transferCurve; float sensitivity; int throttle; //-1 (nkey), 0 (no throttle), 1 (pkey) int dZone;//-32767 .. 32767 int xZone;//-32767 .. 32767 double sumDist; AxisMode mode; //positive keycode int pkeycode; //negative keycode int nkeycode; //the key that is currently pressed int downkey; //the position of the axis, as from jsevent int state; //how long a key should stay down when in gradient mode //note, the key is still clicked at the same pace no matter what, //this just decides how long it stays down each cycle. int duration; QTimer *timer; public slots: void timerCalled(); }; #endif ./qjoypad-4.1.0/src/button.h0000644000175000001440000000325711302332744014456 0ustar johnusers#ifndef BUTTON_H #define BUTTON_H #include #include //for getting a key name in status() #include "keycode.h" //note that the Button class, unlike the axis class, does not need a release //function because it releases the key as soon as it is pressed. class Button : public QObject { Q_OBJECT friend class ButtonEdit; public: Button( int i, QObject *parent = 0 ); ~Button(); //read from stream bool read( QTextStream* stream ); //write to stream void write( QTextStream* stream ); //releases any pushed buttons and returns to a neutral state void release(); //process an event from the actual joystick device void jsevent( int value ); //reset default settings void toDefault(); //True iff is currently using default settings bool isDefault(); //returns a string representation of this button. QString getName() { return "Button " + QString::number(index+1);}; //a descriptive string used as a label for the button representing this axis QString status(); //set the key code for this axis. Used by quickset. void setKey(bool mouse, int value); //happens every MSEC (constant.h) milliseconds void timerTick( int tick ); protected: //true iff this button is physically depressed. bool isButtonPressed; //the index of this button on the joystick int index; //actually sends a key press/release virtual void click( bool press ); //is a simulated key currently depressed? bool isDown; int tick; //button settings bool rapidfire; bool sticky; bool useMouse; int keycode; int mousecode; //like keycode, only mousebutton ;) QTimer *timer; public slots: void timerCalled(); }; #endif ./qjoypad-4.1.0/src/error.h0000644000175000001440000000077511247572303014303 0ustar johnusers#ifndef JOY_ERROR_H #define JOY_ERROR_H #include #include //a nice simple way of throwing up an error message if something goes wrong. static void error(QString type, QString message ) { QMessageBox::warning(0,NAME" - " + type, message, QMessageBox::Ok, Qt::NoButton); } #ifdef _DEBUG static void debug_mesg(const char *fmt, ...) { va_list ap; va_start(ap, NULL); vprintf(fmt, ap); va_end(ap); } #else static void debug_mesg(...) { return; } #endif #endif ./qjoypad-4.1.0/src/joypadw.cpp0000644000175000001440000000600111302332744015141 0ustar johnusers#include "joypadw.h" JoyPadWidget::JoyPadWidget( JoyPad* jp, int i, QWidget* parent ) : QWidget(parent) { //initialize things, build the dialog joypad = jp; index = i; /* This was in below, no idea what it does :( ... * (joypad->axes+1)/2 +(joypad->buttons+1)/2 + 2 */ LMain = new QGridLayout(this); LMain->setSpacing(5); LMain->setMargin(5); flashcount = 0; int insertCounter = 0; quickset = NULL; Axes = new AxisWidget*[joypad->axes]; for (int i = 0; i < joypad->axes; i++) { Axes[i] = new AxisWidget(joypad->Axes[i],this); connect( Axes[i], SIGNAL( flashed( bool ) ), this, SLOT( flash( bool ))); LMain->addWidget(Axes[i], insertCounter / 2, insertCounter %2); insertCounter++; } Buttons = new ButtonWidget*[joypad->buttons]; for (int i = 0; i < joypad->buttons; i++) { Buttons[i] = new ButtonWidget(joypad->Buttons[i],this); connect( Buttons[i], SIGNAL( flashed( bool ) ), this, SLOT( flash( bool ))); LMain->addWidget(Buttons[i], insertCounter/2, insertCounter%2); insertCounter++; } if(insertCounter % 2 == 1) { insertCounter++; } insertCounter+=2; BClear = new QPushButton("Clear", this); connect(BClear, SIGNAL(clicked()), this, SLOT(clear())); LMain->addWidget(BClear, insertCounter / 2, insertCounter %2); insertCounter++; BAll = new QPushButton("Quick Set", this); LMain->addWidget(BAll, insertCounter /2, insertCounter%2); connect(BAll, SIGNAL(clicked()), this, SLOT(setAll())); } JoyPadWidget::~JoyPadWidget() { //so the joypad knows that we're done. joypad->releaseWidget(); } void JoyPadWidget::flash( bool on ) { //true iff this entire widget was considered "flashed" before bool wasOn = (flashcount != 0); //adjust the count based on this new flash flashcount += (on?1:-1); //if we were on and should now be off, or visa versa, flash the whole widget if (wasOn != (flashcount != 0)) emit flashed(index); } void JoyPadWidget::update() { for (int i = 0; i < joypad->axes; i++) { Axes[i]->update(); } for (int i = 0; i < joypad->buttons; i++) { Buttons[i]->update(); } } void JoyPadWidget::clear() { joypad->toDefault(); update(); } void JoyPadWidget::setAll() { //quickset is NULL if there is no quickset dialog, and a pointer to the //dialog otherwise. This is so we can forward jsevents properly. quickset = new QuickSet(joypad); quickset->exec(); update(); delete quickset; quickset = NULL; } void JoyPadWidget::jsevent( js_event msg ) { //notify the component this event applies to. this cannot generate anything //other than a flash :) if (msg.type == JS_EVENT_AXIS) { Axes[msg.number]->jsevent(msg.value); } else { Buttons[msg.number]->jsevent(msg.value); } //if we're doing quickset, it needs to know when we do something. if (quickset != NULL) quickset->jsevent(msg); } ./qjoypad-4.1.0/src/button.cpp0000644000175000001440000001107011302332744015001 0ustar johnusers#include "button.h" #include "event.h" Button::Button( int i, QObject *parent ) : QObject(parent) { index = i; isButtonPressed = false; isDown = false; rapidfire = false; timer = new QTimer(this); toDefault(); tick = 0; } Button::~Button() { release(); //delete timer; } bool Button::read( QTextStream* stream ) { // at this point, toDefault() has just been called. //read in a line of text and break it into words QString input = stream->readLine().toLower(); QRegExp regex("[\\s,]+"); QStringList words = input.split(regex); //used to assure correct conversion of QStrings -> ints bool ok; //used to receive converted ints from QStrings. int val; //go through every word on the line describing this button. for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) { if (*it == "mouse") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= MAXKEY) { useMouse = true; mousecode = val; } else return false; } else if (*it == "key") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= MAXKEY) { useMouse = false; keycode = val; } else return false; } else if (*it == "rapidfire") { rapidfire = true; } else if (*it == "sticky") { sticky = true; } } return true; } void Button::write( QTextStream* stream ) { *stream << "\t" << getName() << ": "; if (rapidfire) *stream << "rapidfire, "; if (sticky) *stream << "sticky, "; *stream << (useMouse?"mouse ":"key ") << (useMouse?mousecode:keycode) << "\n"; } void Button::release() { if (isDown) { click(false); isDown = true; } } void Button::jsevent( int value ) { bool newval = (value == 1); if (sticky) { //the state of a sticky key only changes on button press, not button release. if (value == 1) { isButtonPressed = !isButtonPressed; } else return; } //if the received event indicates a change in state, else if (newval != isButtonPressed) { isButtonPressed = newval; //change state if (isButtonPressed && rapidfire) { tick = 0; connect(timer, SIGNAL(timeout()), this, SLOT(timerCalled())); timer->start(MSEC); } if (!isButtonPressed && rapidfire) { timer->stop(); disconnect(timer, SIGNAL(timeout()), 0, 0); if(isDown) { click(false); } tick = 0; } } //otherwise... we don't care. This shouldn't happen. else return; //if rapidfire is on, then timer() will do its job. Otherwise we must //manually triger the key event. if (!rapidfire) { click(isButtonPressed); } } void Button::toDefault() { rapidfire = false; sticky = false; useMouse = false; keycode = 0; mousecode = 0; timer->stop(); } bool Button::isDefault() { return (rapidfire == false) && (sticky == false) && (useMouse == false) && (keycode == 0) && (mousecode == 0); } QString Button::status() { if (useMouse) { return getName() + " : Mouse " + QString::number(mousecode); } else { return getName() + " : " + QString(ktos(keycode)); } } void Button::setKey( bool mouse, int value ) { if (mouse) { mousecode = value; useMouse = true; } else { keycode = value; useMouse = false; } } void Button::timerTick( int tick ) { if (isButtonPressed) { //originally I just clicked true and then false right after, but this //was not recognized by some programs. I need a delay in between. if (tick % FREQ == 0) { click(true); } if (tick % FREQ == FREQ / 2) { click(false); } } } void Button::click( bool press ) { if (isDown == press) return; isDown = press; xevent click; //determine which of the four possible events we're sending. if (press) click.type = useMouse?BPRESS:KPRESS; else click.type = useMouse?BREL:KREL; //set up the event, click.value1 = useMouse?mousecode:keycode; click.value2 = 0; //and send it. sendevent( click ); } void Button::timerCalled() { timerTick(++tick); } ./qjoypad-4.1.0/src/joypad.h0000644000175000001440000000513011302332744014421 0ustar johnusers#ifndef JOYPAD_H #define JOYPAD_H //parts of the joypad #include "button.h" #include "axis.h" //the widget that will edit this #include "joypadw.h" //for raising errors #include "error.h" //for actually interacting with the joystick devices #include #include #include #include #include #include class JoyPadWidget; //represents an actual joystick device class JoyPad : public QObject{ Q_OBJECT friend class JoyPadWidget; friend class QuickSet; public: void setupJoyDeviceListener(int dev); JoyPad( int i, int dev ); //read from a stream bool readConfig( QTextStream* stream ); //write to a stream void write( QTextStream* stream ); //release any pushed buttons and return to a neutral state void release(); //handle an event from the joystick device this is associated with void jsevent( js_event msg ); //reset to default settings void toDefault(); //true iff this is currently at default settings bool isDefault(); //release the connection to the real joystick void unsetDev(); //read the dimensions on the real joystick and use them void resetToDev( int dev ); //generates a name ("Joystick 1") QString getName() { return "Joystick " + QString::number(index+1);}; //it's just easier to have these publicly available. int joydev; //the actual file descriptor to the joystick device int axes; //the number of axes available on this device int buttons; //the number of buttons //request the joypad to make a JoyPadWidget. We create them this way //so that the joypad is always aware when it has a widget active. JoyPadWidget* widget(QWidget* parent, int i); //called when the joypad is no longer being edited. void releaseWidget(); QString getId(); protected: //lookup axes and buttons. These are dictionaries to support //layouts with different numbers of axes/buttons than the current //devices. Note that with the current layout settings, the defined //buttons that don't actually exist on the device may not be contiguous. QHash Axes; QHash Buttons; //the index of this device (devicenum) int index; //the widget that edits this. Mainly I keep track of this to know if //the joypad is currently being edited. JoyPadWidget* jpw; QSocketNotifier *joydevFileHandle; QSocketNotifier *joydevFileException; QString deviceId; bool hasFocus; public slots: void handleJoyEvents(int fd); void errorRead(int fd); void focusChange(bool windowHasFocus); }; #endif ./qjoypad-4.1.0/src/button_edit.cpp0000644000175000001440000000346711235433371016024 0ustar johnusers#include "button_edit.h" #include #include ButtonEdit::ButtonEdit(Button* butt) :QDialog(0) { setModal(true); //build the dialog! button = butt; setWindowTitle("Set " + button->getName()); setWindowIcon(QPixmap(ICON24)); QVBoxLayout* v = new QVBoxLayout(this); v->setMargin(5); v->setSpacing(5); BKKey = new KeyButton( button->getName(), button->useMouse?button->mousecode:button->keycode, this, true, button->useMouse); v->addWidget(BKKey); QHBoxLayout* h = new QHBoxLayout(); CSticky = new QCheckBox("Sticky", this); CSticky->setChecked(button->sticky); h->addWidget(CSticky); CRapid = new QCheckBox("Rapid Fire", this); CRapid->setChecked(button->rapidfire); h->addWidget(CRapid); v->addLayout(h); h = new QHBoxLayout(); BOkay = new QPushButton("Okay", this); connect(BOkay, SIGNAL( clicked() ), this, SLOT( accept())); h->addWidget(BOkay); BCancel = new QPushButton("Cancel", this); connect(BCancel, SIGNAL( clicked() ), this, SLOT( reject())); h->addWidget(BCancel); v->addLayout(h); } void ButtonEdit::show() { QDialog::show(); setFixedSize(size()); } void ButtonEdit::accept() { //if the rapidfire status has changed, either request a timer or turn it down. /*if (button->rapidfire) { if (!CRapid->isChecked()) tossTimer(button); } else { if (CRapid->isChecked()) takeTimer(button); }*/ button->rapidfire = CRapid->isChecked(); button->sticky = (CSticky->isChecked()); //if the user chose a mouse button... if (BKKey->choseMouse()) { button->useMouse = true; button->mousecode = BKKey->getValue(); } else { button->useMouse = false; button->keycode = BKKey->getValue(); } QDialog::accept(); } ./qjoypad-4.1.0/src/layout.cpp0000644000175000001440000003012311247573006015011 0ustar johnusers#include "layout.h" #include //initialize things and set up an icon :) LayoutManager::LayoutManager( bool useTrayIcon ) { //no LayoutEdit yet. le = NULL; //prepare the popup first. Popup = new QMenu(); fillPopup(); connect(Popup,SIGNAL(triggered(QAction*)),this, SLOT(trayMenu(QAction*))); //make a tray icon if (useTrayIcon) { QSystemTrayIcon *Tray = new QSystemTrayIcon(this); Tray->setContextMenu(Popup); Tray->setIcon(QIcon(ICON24)); Tray->show(); connect(Tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayClick(QSystemTrayIcon::ActivationReason))); } //or make a floating icon else { FloatingIcon* Icon = new FloatingIcon(QPixmap(ICON64),Popup,0,"tray"); connect(Icon, SIGNAL( clicked()), this, SLOT( iconClick())); connect(Icon, SIGNAL( closed()), qApp, SLOT( quit())); Icon->show(); } //no layout loaded at start. setLayoutName(NL); } QString LayoutManager::getFileName( QString layoutname ) { return settingsDir + layoutname + ".lyt"; } bool LayoutManager::load(const QString& name) { //it's VERY easy to load NL :) if (name == NL) { clear(); return true; } QFile file(getFileName(name)); //if the file isn't available, if (!file.exists()) { error("Load error","Failed to find a layout named " + name); return false; } //if the file isn't readable, if (!file.open(QIODevice::ReadOnly)) { error("Load error","Error reading from file " + file.fileName()); return false; } //reset all the joypads. //note that we don't use available here, but joypads instead. This is so //if one layout has more joypads than this one does, this won't have the //extra settings left over after things are supposed to be "cleared" QHashIterator it( joypads ); while (it.hasNext()) { it.next(); it.value()->toDefault(); } //start reading joypads! QTextStream stream( &file ); QString input = stream.readLine().toLower(); QRegExp quoted("\"(.*)\""); bool okay; int num; while (input != QString::null) { QStringList words = input.split(" "); //if this line is specifying a joystick if (words[0] == "joystick") { num = words[1].toInt(&okay); //make sure the number of the joystick is valid if (!okay || okay < 1) { error( "Load error", "Error reading joystick definition. Expected: Joysyick 1 {"); if (name != CurrentLayout) reload(); else clear(); return false; } //if there was no joypad defined for this index before, make it now! if (joypads[num-1] == 0) { joypads.insert(num-1, new JoyPad(num-1, 0)); } //try to read the joypad, report error on fail. if (!joypads[num-1]->readConfig(&stream)) { error( "Load error", "Error reading definition for joystick " + QString::number(num-1)); //if this was attempting to change to a new layout and it failed, //revert back to the old layout. if (name != CurrentLayout) reload(); //to keep from going into an infinite loop, if there is no good //layout to fall back on, go to NL. else clear(); return false; } } //read a new line. input = stream.readLine().toLower(); } //if loading succeeded, this is our new layout. setLayoutName(name); return true; } bool LayoutManager::load() { //try to load the file named "layout" to retrieve the last used layout name QFile file( settingsDir + "layout"); QString name; if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); name = stream.readLine(); file.close(); //if there was no name, don't load. if (name == "") { return false; } //if there was a name, try to load it! Note, this will still return //false if the name is invalid ( see load() ) return load(name); } //if the file isn't available to open, don't load. return false; } bool LayoutManager::reload() { return load(CurrentLayout); } void LayoutManager::clear() { //reset all the joypads... QHashIterator it (joypads); while (it.hasNext()) { it.next(); it.value()->toDefault(); } //and call our layout NL setLayoutName(NL); } void LayoutManager::save() { if (CurrentLayout == NL) { saveAs(); return; } //get a filename QString filename = getFileName( CurrentLayout ); QFile file(filename); //if it's good, start writing the file if (file.open(QIODevice::WriteOnly)) { QTextStream stream( &file ); stream << "# "NAME" Layout File\n\n"; QHashIterator it (joypads); while (it.hasNext()) { it.next(); it.value()->write( &stream ); } file.close(); } //if it's not, error. else error("Save error", "Could not open file " + filename + ", layout not saved."); } void LayoutManager::saveAs() { bool ok; //request a new name! QString name = QInputDialog::getText(0, NAME" - Name new layout","Enter a name for the new layout:", QLineEdit::Normal, QString::null, &ok ); if (!ok) { return; } QFile file(settingsDir + name + ".lyt"); //don't overwrite an existing layout. if (file.exists()) { error("Save error", "That name's already taken!"); return; } //now that the new layout has a name, that is the name we should use. setLayoutName(name); //since we have a new name for this layout now, we can save it normally :) save(); //add the new name to our lists fillPopup(); if (le != NULL) { le->updateLayoutList(); } } void LayoutManager::saveDefault() { QFile file( settingsDir + "layout"); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream << CurrentLayout; file.close(); } } void LayoutManager::remove() { if (CurrentLayout == NL) return; if (QMessageBox::warning( 0, NAME" - Delete layout?","Remove layout permanently from your hard drive?", "Yes", "No", 0, 0, 1 ) == 1) return; QString filename = getFileName( CurrentLayout ); if (!QFile(filename).remove()) { error("Remove error", "Could not remove file " + filename); } fillPopup(); if (le != NULL) { le->updateLayoutList(); } clear(); } QStringList LayoutManager::getLayoutNames() { //goes through the list of .lyt files and removes the file extensions ;) QStringList result = QDir(settingsDir).entryList(QStringList("*.lyt")); for ( QStringList::Iterator it = result.begin(); it != result.end(); ++it ) { *it = (*it).left((*it).length() - 4); } //and, of course, there's always NL. result.prepend(NL); return result; } void LayoutManager::setLayoutName(QString name) { CurrentLayout = name; fillPopup(); if (le != NULL) { le->setLayout(name); } } void LayoutManager::iconClick() { //don't show the dialog if there aren't any joystick devices plugged in if (available.count() == 0) { error("No joystick devices available","No joystick devices are currently available to configure.\nPlease plug in a gaming device and select\n\"Update Joystick Devices\" from the popup menu."); return; } if(le) { return; } //otherwise, make a new LayoutEdit dialog and show it. le = new LayoutEdit(this); le->setLayout(CurrentLayout); } void LayoutManager::trayClick(QSystemTrayIcon::ActivationReason reason) { if(reason == QSystemTrayIcon::Trigger) { iconClick(); } } void LayoutManager::trayMenu(QAction *menuItemAction) { //if they clicked on a Layout name, load it! //note that the other options are handled with their own special functions if (Popup->actions().indexOf(menuItemAction) > 1 && menuItemAction->text() != "Quit" && menuItemAction->text() != "Update lyaout list" && menuItemAction->text() != "Update joystick devices") { load(menuItemAction->text()); } } void LayoutManager::fillPopup() { //start with an empty slate Popup->clear(); //make a list of joystick devices QString devs = "Joysticks: "; QHashIterator it( available ); while (it.hasNext()) { it.next(); devs += QString::number(it.key() + 1) + " "; } QAction *temp = Popup->addAction(devs); Popup->addSeparator(/*temp*/); //add in the Update options QAction *tempAdd = new QAction("Update layout list", this); connect(tempAdd, SIGNAL(triggered(bool)), this, SLOT(fillPopup())); Popup->addAction(tempAdd); tempAdd = new QAction("Update joystick devices", this); connect(tempAdd, SIGNAL(triggered(bool)), this, SLOT(updateJoyDevs())); Popup->addAction(tempAdd); Popup->addSeparator(/*temp*/); //then add all the layout names QStringList names = getLayoutNames(); for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it ) { temp = Popup->addAction(*it); temp->setCheckable(true); //put a check by the current one ;) if (CurrentLayout == (*it)) { temp->setChecked(true); } } Popup->addSeparator(); //and, at the end, quit! Popup->addAction("Quit",qApp,SLOT(quit())); } void LayoutManager::updateJoyDevs() { debug_mesg("updating joydevs\n"); QString devdir = DEVDIR; //reset all joydevs to sentinal value (-1) do { QHashIterator it( joypads ); while (it.hasNext() ) { it.next(); it.value()->unsetDev(); } } while (0); //clear out the list of previously available joysticks available.clear(); //set all joydevs anew (create new JoyPad's if necesary) QDir DeviceDir(devdir); QStringList devices = DeviceDir.entryList(QStringList("js*"), QDir::System ); QRegExp devicename(".*\\js(\\d+)"); int joydev; int index; //for every joystick device in the directory listing... //(note, with devfs, only available devices are listed) for (QStringList::Iterator it = devices.begin(); it != devices.end(); ++it) { debug_mesg("found a device file, %s\n", qPrintable(devdir + "/" + (*it))); //try opening the device. joydev = open( qPrintable(devdir + "/" + (*it)), O_RDONLY | O_NONBLOCK); //if it worked, then we have a live joystick! Make sure it's properly //setup. if (joydev > 0) { devicename.indexIn(*it); index = QString(devicename.cap(1)).toInt(); JoyPad* joypad; //if we've never seen this device before, make a new one! if (joypads[index] == 0) { struct pollfd read_struct; read_struct.fd = joydev; read_struct.events = POLLIN; char buf[10]; while(poll(&read_struct, 1, 5)!=0) { debug_mesg("reading junk data\n"); read(joydev, buf, 10); } joypad = new JoyPad( index, joydev ); joypads.insert(index,joypad); } else { debug_mesg("found previously open joypad with index %d, ignoring", index); joypad = joypads[index]; joypad->resetToDev(joydev); } //make this joystick device available. available.insert(index,joypad); } else { int errsv = errno; printf("error reading joypad dev device: %s\n", strerror(errsv)); } } //when it's all done, rebuild the popup menu so it displays the correct //information. fillPopup(); if(le) { le->updateJoypadWidgets(); } debug_mesg("done updating joydevs\n"); } void LayoutManager::leWindowClosed() { le=NULL; } ./qjoypad-4.1.0/src/device.h0000644000175000001440000000067611235433371014407 0ustar johnusers#ifndef JOY_DEVICE_H #define JOY_DEVICE_H #include "joypad.h" //the purpose of this file is to make device information available to what //needs it. //a collection of joysticks currently available on this computer extern QHash available; //a collection of joypad objects representing all the available joysticks //as well as the ones defined in a layout buy not currently plugged in. extern QHash joypads; #endif ./qjoypad-4.1.0/src/layout_edit.h0000644000175000001440000000202611311571343015456 0ustar johnusers#ifndef LAYOUT_EDIT_H #define LAYOUT_EDIT_H #include //for the tab list of joypads #include "flash.h" //this is a front end for the non-GUI LayoutManager class LayoutEdit; #include "layout.h" //so we can see the available devices #include "device.h" //so we can use the LayoutEditer to edit key layouts :) #include "joypadw.h" class LayoutManager; class LayoutEdit : public QWidget { Q_OBJECT public: LayoutEdit( LayoutManager* l ); //swap to a new layout void setLayout(QString layout); //update the list of available layouts void updateLayoutList(); void updateJoypadWidgets(); signals: void focusStateChanged(bool); public slots: void appFocusChanged(QWidget *old, QWidget *now); protected: //the layout manager this represents LayoutManager* lm; virtual void closeEvent(QCloseEvent *event); //parts of the dialog: QVBoxLayout *LMain; QStackedWidget *PadStack; FlashRadioArray *JoyButtons; QComboBox* CLayouts; QPushButton *BAdd, *BRem, *BUpd, *BRev; }; #endif ./qjoypad-4.1.0/src/axis_edit.h0000644000175000001440000000221711254060314015104 0ustar johnusers#ifndef AXIS_EDIT_H #define AXIS_EDIT_H //to refer to the axis we're editing //for building up the dialog we need #include "axis.h" #include #include #include #include #include //for my home-brewed widgets #include "joyslider.h" #include "keycode.h" class AxisEdit : public QDialog { Q_OBJECT public: AxisEdit(Axis* ax); //show the dialog (modal) void show(); //set the current state of the axis (adjusts the JoySlider for real time //notification of the state to the user) void setState( int val ); protected slots: //slots for GUI events void CGradientChanged( bool on ); void CModeChanged( int index ); void CTransferCurveChanged( int index ); void CThrottleChanged( int index ); void accept(); protected: //the associated Axis that needs to be set. Axis *axis; //the important parts of the dialog: QCheckBox *CGradient; QComboBox *CMode, *CThrottle, *CTransferCurve; QFrame *MouseBox, *KeyBox; QSpinBox *SSpeed; QLabel *LSensitivity; QDoubleSpinBox *SSensitivity; KeyButton *BNeg, *BPos; JoySlider *Slider; QPushButton *BOkay, *BCancel; }; #endif ./qjoypad-4.1.0/src/axis.cpp0000644000175000001440000002447611332164615014453 0ustar johnusers#include "axis.h" #include "event.h" #include "time.h" #define sqr(a) ((a)*(a)) #define cub(a) ((a)*(a)*(a)) #define clamp(a, a_low, a_high) \ ((a) < (a_low) ? (a_low) : (a) > (a_high) ? (a_high) : (a)) Axis::Axis( int i, QObject *parent ) : QObject(parent) { index = i; isOn = false; isDown = false; state = 0; gradient = false; toDefault(); tick = 0; timer = new QTimer(this); } Axis::~Axis() { release(); delete timer; } bool Axis::read( QTextStream* stream ) { // At this point, toDefault has just been called. //read in a line from the stream, and split it up into individual words QString input = stream->readLine().toLower(); QRegExp regex("[\\s,]+"); QStringList words = input.split(regex); //used to assure QString->int conversions worked bool ok; //int to store values derived from strings int val; float fval; //step through each word, check if it's a token we recognize for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) { if (*it == "maxspeed") { ++it; //move to the next word, which should be the maximum speed. //if no value was given, there's an error in the file, stop reading. if (it == words.end()) return false; //try to convert the value. val = (*it).toInt(&ok); //if that worked and the maximum speed is in range, set it. if (ok && val >= 0 && val <= MAXMOUSESPEED) maxSpeed = val; //otherwise, faulty input, give up. else return false; } //pretty much the same process for getting the dead zone else if (*it == "dzone") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= JOYMAX) dZone = val; else return false; } //and again for the extreme zone, else if (*it == "xzone") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= JOYMAX) xZone = val; else return false; } else if (*it == "tcurve") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= power_function) transferCurve = val; else return false; } else if (*it == "sens") { ++it; if (it == words.end()) return false; fval = (*it).toFloat(&ok); if (ok && fval >= SENSITIVITY_MIN && fval <= SENSITIVITY_MAX) sensitivity = fval; else return false; } //and for the positive keycode, else if (*it == "+key") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= MAXKEY) pkeycode = val; else return false; } //and finally for the negative keycode. else if (*it == "-key") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= MAXKEY) nkeycode = val; else return false; } //the rest of the options are keywords without integers else if (*it == "gradient") { gradient = true; } else if (*it == "throttle+") { throttle = 1; } else if (*it == "throttle-") { throttle = -1; } else if (*it == "mouse+v") { mode = mousepv; } else if (*it == "mouse-v") { mode = mousenv; } else if (*it == "mouse+h") { mode = mouseph; } else if (*it == "mouse-h") { mode = mousenh; } //we ignore unrecognized words to be friendly and allow for additions to //the format in later versions. Note, this means that typos will not get //the desired effect OR produce an error message. } //assume that xZone, dZone, or maxSpeed has changed, for simplicity. //do a few floating point calculations. adjustGradient(); //if we parsed through all of the words, yay! All done. return true; } void Axis::timerCalled() { timerTick(++tick); } void Axis::write( QTextStream* stream ) { *stream << "\t" << getName() << ": "; if (gradient) *stream << "gradient, "; if (throttle > 0) *stream << "throttle+, "; else if (throttle < 0) *stream << "throttle-, "; if (dZone != DZONE) *stream << "dZone " << dZone << ", "; if (xZone != XZONE) *stream << "xZone " << xZone << ", "; if (mode == keybd) { *stream << "+key " << pkeycode << ", " << "-key " << nkeycode << "\n"; } else { if (gradient) *stream << "maxSpeed " << maxSpeed << ", "; if (transferCurve != quadratic) *stream << "tCurve " << transferCurve << ", "; if (sensitivity != 1.0F) *stream << "sens " << sensitivity << ", "; *stream << "mouse"; if (mode == mousepv) *stream << "+v\n"; else if (mode == mousenv) *stream << "-v\n"; else if (mode == mouseph) *stream << "+h\n"; else if (mode == mousenh) *stream << "-h\n"; } } void Axis::release() { //if we're pressing a key, let it go. if (isDown) { move(false); isDown = false; } } void Axis::jsevent( int value ) { //adjust real value to throttle value if (throttle == 0) state = value; else if (throttle == -1) state = (value + JOYMIN) / 2; else state = (value + JOYMAX) / 2; //set isOn, deal with state changing. //if was on but now should be off: if (isOn && abs(state) <= dZone) { isOn = false; if (gradient) { duration = 0; release(); timer->stop(); disconnect(timer, SIGNAL(timeout()), 0, 0); tick = 0; } } //if was off but now should be on: else if (!isOn && abs(state) >= dZone) { isOn = true; if (gradient) { duration = (abs(state) * FREQ) / JOYMAX; connect(timer, SIGNAL(timeout()), this, SLOT(timerCalled())); timer->start(MSEC); } } //otherwise, state doesn't change! Don't touch it. else return; //gradient will trigger movement on its own via timer(). //non-gradient needs to be told to move. if (!gradient) { move(isOn); } } void Axis::toDefault() { release(); gradient = false; throttle = 0; maxSpeed = 100; transferCurve = quadratic; sensitivity = 1.0F; dZone = DZONE; tick = 0; xZone = XZONE; mode = keybd; pkeycode = 0; nkeycode = 0; downkey = 0; state = 0; adjustGradient(); } bool Axis::isDefault() { return (gradient == false) && (throttle == 0) && (maxSpeed == 100) && (dZone == DZONE) && (xZone == XZONE) && (mode == keybd) && (pkeycode == 0) && (nkeycode == 0); } bool Axis::inDeadZone( int val ) { int value; if (throttle == 0) value = val; else if (throttle == -1) value = (val + JOYMIN) / 2; else value = (val + JOYMAX) / 2; return (abs(value) < dZone); } QString Axis::status() { QString result = getName() + " : ["; if (mode == keybd) { if (throttle == 0) result += "KEYBOARD"; else result += "THROTTLE"; } else result += "MOUSE"; return result + "]"; } void Axis::setKey(bool positive, int value) { if (positive) pkeycode = value; else nkeycode = value; } void Axis::timerTick( int tick ) { if (isOn) { if (mode == keybd) { if (tick % FREQ == 0) { if (duration == FREQ) { if (!isDown) move(true); duration = (abs(state) * FREQ) / JOYMAX; return; } move(true); } if (tick % FREQ == duration) { move(false); duration = (abs(state) * FREQ) / JOYMAX; } } else { move(true); } } } void Axis::adjustGradient() { inverseRange = 1.0F / (xZone - dZone); // This is also the convenient spot to initialize the dithering // accmulator. sumDist = 0; } void Axis::move( bool press ) { xevent e; if (mode == keybd) { //prevent KeyPress-KeyPress and KeyRelease-KeyRelease pairs. //this would only happen in odd circumstances involving the setup //dialog being open and blocking events from happening. if (isDown == press) return; isDown = press; if (press) { e.type = KPRESS; downkey = (state > 0)?pkeycode:nkeycode; } else { e.type = KREL; } e.value1 = downkey; e.value2 = 0; } //if using the mouse else if (press) { int dist; if (gradient) { const int absState = abs(state); float fdist; // Floating point movement distance if (absState >= xZone) fdist = 1.0F; else if (absState <= dZone) fdist = 0.0F; else { const float u = inverseRange * (absState - dZone); switch(transferCurve) { case quadratic: fdist = sqr(u); break; case cubic: fdist = cub(u); break; case quadratic_extreme: fdist = sqr(u); if(u >= 0.95F) { fdist *= 1.5F; } break; case power_function: fdist = clamp(powf(u, 1.0F / clamp( sensitivity, 1e-8F, 1e+3F)), 0.0F, 1.0F); break; default: fdist = u; } } fdist *= maxSpeed; if (state < 0) fdist = -fdist; // Accumulate the floating point distance and shift the // mouse by the rounded magnitude sumDist += fdist; dist = static_cast(rint(sumDist)); sumDist -= dist; } //if not gradient, always go full speed. else dist = maxSpeed; e.type = WARP; if (mode == mousepv) { e.value1 = 0; e.value2 = dist; } else if (mode == mousenv) { e.value1 = 0; e.value2 = -dist; } else if (mode == mouseph) { e.value1 = dist; e.value2 = 0; } else if (mode == mousenh) { e.value1 = -dist; e.value2 = 0; } } //actually create the event sendevent(e); } ./qjoypad-4.1.0/src/buttonw.h0000644000175000001440000000106511235433371014643 0ustar johnusers#ifndef BUTTON_WIDGET_H #define BUTTON_WIDGET_H //this represents a Button #include "button.h" //this can set a button using a ButtonEdit #include "button_edit.h" //this IS a FlashButton #include "flash.h" #ifdef Bool #undef Bool #endif #include class ButtonWidget : public FlashButton { public: ButtonWidget( Button* b, QWidget* parent); void jsevent( int val ); //reset the label to match the respective Button's current state. void update(); private: void mouseReleaseEvent( QMouseEvent* e ); bool on; Button* button; }; #endif ./qjoypad-4.1.0/src/flash.h0000644000175000001440000000350311235433371014235 0ustar johnusers#ifndef FLASH_H #define FLASH_H //The color the buttons flash! Feel free to change this. //The three numbers are all you need to touch, and they //represent the red, green, and blue values of the color, //respectively. #define HIGHLIGHT QColor( 0,0,255 ) //--- --- --- --- End of editable code. #include #include #include //A QPushButton that can flash a color //The color it flashes is defined by HIGHLIGHT //A FlashButton can now also be set dark //in the same way that it can flash, but //a FlashButton can not be dark and flash at the same time. class FlashButton : public QPushButton { Q_OBJECT public: FlashButton( QString text, QWidget* parent, QString name = "" ); public slots: //make the button turn blue if it was gray, or visa versa. void flash(); signals: //let the world know when flash() happens! void flashed( bool on ); private: //is this currently "flashed" (ie, colored blue)? bool isflash; //the normal, unflashed palette QPalette Normal; //the colorful flashed palette QPalette Flash; }; //An array of flashing mutually-exclusive toggle buttons. class FlashRadioArray : public QWidget { Q_OBJECT public: FlashRadioArray( int count, QString names[], bool horizontal, QWidget* parent); //returns an integer returning the currently selected button. //First button is 0. int getState(); public slots: //flash the button at the given index void flash( int ); private slots: //this is what happens when one of the buttons in the array is clicked. void clicked(); signals: //happens when the state changes. The int is the new state. void changed( int ); private: //how many buttons int Count; //which is currently down int State; //the array of buttons FlashButton** Buttons; //the main layout. QBoxLayout* LMain; }; #endif ./qjoypad-4.1.0/src/layout.h0000644000175000001440000000514111311544662014456 0ustar johnusers#ifndef JOY_LAYOUT_H #define JOY_LAYOUT_H //to allow for interprocess communications (ie, signaling a running instance of //qjoypad by running "qjoypad layout-name", etc.) QJoyPad uses signals to //triger certain events. This is for signaling the main program to update //the joystick device list. #include #include #include #include #include #include #include #include #include //a layout handles several joypads #include "joypad.h" //for errors #include "error.h" //so we know which joypads have RL equivalents #include "device.h" //For displaying a floating icon instead of a tray icon #include "icon.h" //So we can know if there is a graphical version of the Layout Manager displayed #include "layout_edit.h" //for recognizing when the special empty layout is in use #define NL "[NO LAYOUT]" //where QJoyPad saves its settings! const QString settingsDir(QDir::homePath() + "/.qjoypad3/"); //handles loading, saving, and changing of layouts class LayoutManager : public QObject { friend class LayoutEdit; Q_OBJECT public: LayoutManager( bool useTrayIcon); //produces a list of the names of all the available layout. QStringList getLayoutNames(); void leWindowClosed(); public slots: //load a layout with a given name bool load(const QString& name); //look for the last loaded layout and try to load that. bool load(); //load the current layout, overwriting any changes made to it. bool reload(); //reset to a blank, empty layout void clear(); //save the current layout with its current name void save(); //save the current layout with a new name void saveAs(); //save the currently loaded layout so it can be recalled later void saveDefault(); //get rid of a layout void remove(); //when the tray icon is clicked void iconClick(); void trayClick(QSystemTrayIcon::ActivationReason reason); //when the user selects an item on the tray's popup menu void trayMenu(QAction* menuItemAction); //rebuild the popup menu with the current information void fillPopup(); //update the list of available joystick devices void updateJoyDevs(); private: //change to the given layout name and make all the necesary adjustments void setLayoutName(QString name); //get the file name for a layout name QString getFileName( QString layoutname ); //the layout that is currently in use QString CurrentLayout; //the popup menu from the tray/floating icon QMenu* Popup; //if there is a LayoutEdit open, this points to it. Otherwise, NULL. LayoutEdit* le; }; #endif ./qjoypad-4.1.0/src/flash.cpp0000644000175000001440000000476611235433371014604 0ustar johnusers#include "flash.h" //Added by qt3to4: //Modified here (and in .h) to not have default arguments for 2 and 3. //This caused an error with a development version of g++ on a Mandrake system //in Sweden. FlashButton::FlashButton( QString text, QWidget* parent, QString name ) :QPushButton( text, parent ) { this->setObjectName(name); //record the base palette for posterity. Normal = palette(); //define the palette the button will have when it flashes. QPalette cg = this->palette(); cg.setCurrentColorGroup(QPalette::Inactive); cg.setColor(QPalette::Button, HIGHLIGHT); cg.setColor(QPalette::Light, HIGHLIGHT.light(150)); cg.setColor(QPalette::Midlight, HIGHLIGHT.light(125)); cg.setColor(QPalette::Dark, HIGHLIGHT.dark(200)); cg.setColor(QPalette::Mid, HIGHLIGHT.dark(150)); Flash = cg; isflash=false; setAutoDefault( false ); setFocusPolicy(Qt::NoFocus); } void FlashButton::flash() { emit( flashed( !isflash ) ); if (isflash) { setPalette( Normal ); isflash = false; } else { setPalette( Flash ); isflash = true; } } FlashRadioArray::FlashRadioArray( int count, QString names[], bool horizontal, QWidget* parent) :QWidget( parent ) { if (horizontal) { LMain = new QHBoxLayout( this); LMain->setMargin(5); LMain->setSpacing(5); } else { LMain = new QVBoxLayout( this); LMain->setMargin(5); LMain->setSpacing(5); } Buttons = new FlashButton*[count]; for (int i = 0; i < count; i++) { Buttons[i] = new FlashButton( names[i], this, "" ); //when any of the buttons is clicked, it calls the same function on this. connect( Buttons[i], SIGNAL( clicked() ), this, SLOT( clicked() )); LMain->addWidget(Buttons[i]); } Count = count; State = 0; Buttons[0]->setDown( true ); } int FlashRadioArray::getState() { return State; } void FlashRadioArray::flash( int index ) { Buttons[index]->flash(); } void FlashRadioArray::clicked() { //go through each button. If it wasn't the button that was just clicked, //then make sure that it is up. If it WAS the button that was clicked, //remember that index as the new state. for (int i = 0; i < Count; i++) { if ( Buttons[i] != sender() ) Buttons[i]->setDown( false ); else { State = i; Buttons[i]->setDown( true ); } } emit changed( State ); } ./qjoypad-4.1.0/INSTALL.txt0000644000175000001440000000227411315257566014064 0ustar johnusersFor a quick install, just follow these steps: tar -xzvf qjoypad-4.?.tgz cd qjoypad-4.?/src ./config make make install If those steps don't work for you, here are some common problems: Compilations fails with along the lines of: axis.h:21: error: expected class-name before ‘{’ token axis.h:22: error: ISO C++ forbids declaration of ‘Q_OBJECT’ with no type axis.h:24: error: expected ‘;’ before ‘friend’ axis.h:29: error: ‘QTextStream’ has not been declared axis.h:31: error: ‘QTextStream’ has not been declared axis.h:40: error: ‘QString’ does not name a type axis.h:44: error: ‘QString’ does not name a type axis.h:89: error: ISO C++ forbids declaration of ‘QTimer’ with no type Most likely you're using the Qt3 version of qmake; QJoyPad needs to use Qt4 to work properly. The config script is supposed to detect this properly, but in the event it fails you should find what the proper executable is (if you think you've found it run it with the --version option to make sure it is for Qt 4.0 or greater). Then rerun the config script with the --qmake4bin=BIN option, where BIN is the executable name of the proper qmake binary, or a path to the proper qmake binary. ./qjoypad-4.1.0/icons/0000755000175000001440000000000011332170105013301 5ustar johnusers./qjoypad-4.1.0/icons/gamepad3-64x64.png0000644000175000001440000001215411160766732016304 0ustar johnusersPNG  IHDR@@iqbKGD pHYs  ~tIME 5*aրIDATxiyξܕ˽\Sdl-b''E$F5`ĭkZh.A? InkH%J(k MHs̝yssN?#rjE ?,9aM`Sl4O 6Fx# cdvz\+ szZ5OHo~ʖLL8.φ\V6y#<1v{?~(L[L]$j20<̱ KjZO/~a8"ifƯnr}ve`V}C p ˲;:X@4ƘtCtY] ,UM y(~=;Ɵ~?hc6@ZW?۔6mǶm\ʅSOi>3qD^=}0 |c N+V6]{? xXV 0;4r bp0{FO!`ZkV—߰8X P׹#eM*Mކ1IZ=,z?[w\YdfA,˥Cr\@86%)s L}[&Z Oһu'QVX)vXLO^a<㆔} !h6W(K{N.qH%7mbۮw; R! Z[ƣHcsSnof«=C[} s>BH. THfXi,RR\XQ)er2j Ʋl-aIu8W[w ̗lCA@P* 9raX^m292 owfCX/q;6ccecaafely>*=5<66s9(Ȭ9¶-hc2t]0 (CJŀR1O1ò,, D03='o mb6 }Ǿ-¶mV.{!/^fa SJ=TER&,7<򂂥sQ̗+{%fgtwn(TzChM.'r Kq۲mh5˾T+k5d&\8MXmX"TljʹksL`Ð<Gn8C 9 OX86@'_j0Bc; xtcjuc(}nyQxulJ*R(9IV>8iZ'bbfK:d%,-Y״; stk8~ǀN}=mch`eX8뺸~7-Bjz>ϹWZ2_ Bȡ;S QԡR.ؽ:.q|,ic2xAcj`1.FN'a2aD܊Rt~~7N<hJё6&Adm `Y؎8:A@>'! 0?=ֻi~ZsDQ/PVV<,vZfJxű;Cx=gXcP*CMi3ye)e<@kEd(Bkt+mJt DZZpMC"brc|6@qׯ~r)s"Aqst;\mlgv|bNVk8 JIC>s%YVM{`07 '_WgfvZYW+hVLf݅+I&/%^V$C72͛(W atR:WPPב&G"pho3 a)Ǧ^^5% \ۼxArBg.i-|M$UTem2UMf=_wD"RR{uFpзL[<)Ro_OQh.wlq](ˡh<5NIK /.\jqr>q|?$=DP,Wh./kؾKF/VPZ\weVY%Y&ȤXgdRB XL)4K}}zmcP*hCTa]z߭~$IㄹhLgNǢق[,nvH nkm_g}aVN1~jh0FwKyvIJm!H!"!M{(J؎xOw[m}4M8tj%̇;66xrM LB E.>@Q,fvvpoKq=Tj{'=rOX6^P i,.CO/Q aXCT&iγ0EmaN#E)"DAPqut:1w|=?ZO. svI"2Ȳ $Q'bu~lrGЖ)BY<עh!$fHhh6Z.05JeKuٶ&6o^'^dq%tvG\B1V-JedR!Be(H(9xMc|};>Yַ|9T&0Nd2!qҔb5%[ k6Q{oS9;Jne!3M"PHQ\ix%ŕӜy~ڭyv]r<P!$IfgftU'/n,baJRop2ijD1_Ou;Ib<(+|?MwStZ]h J 2t9gf]Ij2ILi3:QMTIBczr} Ji,,\ϥG7"a;>,.,0=vY2a.a|BO}/CϟXĉ8}{)4vu$a>:(itصs;#qn{'gq= }hG Q!1օƶm'Qq.^03>F7 %r2Z/{|ؖ҆4d7y7uoxoK?V8 IeVu=Ak2Ei]##"S(nX+Ʌa')B dfZu&2Qy!q=,fz',Vɕj' UBru~>0*ˋſs={s'?˙xԷhM`tnKht2c޻L5~!c"JHE I,I4 EL"tZM@MRPQ(u"F jE!: J|~]xGm#kDi"he;pnZi\,%4Q,HI 4A$1BqWu8w`;P^~ϱsjfTIEDD)i"2!<^*}OنRy':NKK q)JdkX/K3VWp"rRL$4NH6I!)зi͇y;^9{M$ ogPK}`aX=f&/2w˧yŧmE\;>M2vUΜzwuisبLD y !$R(D1"H"҈ |01~+~TZ*dR I,lW簄P)cBKVm |T遉W^e0خ9rmX7Cƍ&Kn՝ǜ3p2o*䲥rZqds2~cǻ;*_B ‹>QWo = ,_>p /}Ͽ<PQuPy4BO`art0t{O9].y>'sZN  <.~ӗ^~}Oz"UJ Ew k7^BCPP|_s !n,߸9_/ÚE*D(<"E{ ~-"Aw&햾/ű ty'Ko޻4Yz/JϋkN/ y=q `j]vwp&+0EJ(@} =ҘT" T=B*o!lTx@Zզl>n 뵚Y_Ywdd=Y zG|nH0^$=q 8 Hy3phיwA;~pkVB?'vc~V<_1G Ԁ EUU5' 0dJI|3J1Q*Å;gKk/{w~վ:0fy`3=YXJhשvas86F~%I 9'bD0*FjgVkH0#`4b|\Mz߉Ư YSbN }f0V}X:HD׊"[=89X;ΒRq1("S ̤hRwiT6FFL Q (N66?؏7Z,O>7YF{ @"2j/]~>M&+vPTE u`󘧈:1Zd6~?w8q3RJP~i:ơAo 5rn sF:o<@ԁeR^={mh@4PEL(ZiT)D$WuHDMdvp;;ܙڭheeu2nUSȘ[En5OC j]~[8\w.tY4Թ.svp< x~@ET@] 8UU*PuHj %p6'\۠?5z#Y. b,АDSJ]e+g&wnl=^QaU ;&$i#)~/׿u#*P5).FP!G]^rbUXufDǫKk;kk{+2Bd 2ӑ޸\*HwSEQrȧǓ Qm.9qe1bn}$/CC VdJƬmT99=DY4عr]XZȢ=t*.@UUJYme.G>~nuk͵a2T#t>]aZ݋^.jժBh#/ׂ$%-ԑ=ׯxx~^gQX%vyw}Aqj>oeds;Bx8@ JN$Ix:)$ˋAR$Adt8'nӻzٕKW/:8Y19Iu(n^g01}!a|:۩hԸs{z7M<ڢ"0YUš8C_0r,4Ԝ5Ms_@D"0VDy\TYN;w^+7Z| <"F08ٜO1eΦ\cfӜPD(9yǁ<8 T|,Z){Aԕ)%EW5Ρ8qZ8fEsOgd{ؼoZ*I|ygUAE$|"c6u%{ۼ񍯽Zx>nnPNvwÈUQETp(Z,Ȱ[8b4AC9X(R.C\,P-׆I-ˊd-cD8xuB1~g=⮏ns)*M3,#b|d (r.;^n2 ?'R!YnbUh CYY^  H/둻bͳB빸HqƸNx>FDJNcp*ƈ,RfRj>[4MP͍Mru.)ͰMhs2ۺ͕4G c2p4sZb`lF~` "`:I^dQV_zɝ+G;i0 <ε%. 444M% |Yvݨk7~VX?[y9'"b)H$I1c$զix2c 2/lTdS{aӈ+u\t`J-Zemeے^8 R-9be,-Mwgn$$ϣ$<% ILSY( qH>KyG{h\ .fpCA](fQ,I)I?̕;""D@UF\n\ûKIǹ%J`jjYqZS#ƫժߛԞtF൘IrD8흠\"mo6==ҺO\/0REUf:f+d<2loйza,J1PS_mn]ݼ4v>={Y[pm~ֲVBߛ+_yvw =(2 pyZRăj( yBT)Z뵑BKw^~Te8 @z*rVj|}wi{h|rkjbug9,#fǝ{Ѿ=Gڳr^ZCq{lꪡ)^zrޭ^oz(Dě$F+5۵٨e"n:i\ۨꙈߔ4?랋dPF6w'?kGۗdjյ(I Is/MsZ<#aA[ϗ"%i2K?Gtgmuy/Ɠ_ܓXZ(ե_~d2\ɋm1b|<{N\gEiQSUhtn~÷|gV}8$4~k/PU&UpAoÏ[{pkEZ͋,,sb:Ϊy^0k;W_y=Y^^=2r0,@ey>5@p1H tAvǓq`o]=99 {S2HQqZͮ]_nxeqyԨ =JWQQF# ӌuFI©(_.#%E$4p*bηyi \Ej8_i.6jG@\ ʣjKD2Дrn§,?KwE"B,XHeƀǵtO'`<˿^]яߜ)Okϼ˔_V{r} m=ۉl[~IENDB`./qjoypad-4.1.0/icons/gamepad1-24x24.png0000644000175000001440000000202611160766732016267 0ustar johnusersPNG  IHDRש4PLTEBF>v®FNBڊ^jRnv^ꞪrB.VRn¾r~ffjzƲF6rjrZbbfzzrvjN>nzb~vjb~ºnvf¾bnZrrnrvrξ~v~r~zjnf҆VB~ʾ²rrvznRFږƺڮnrjv~zfj^ʢ~򒊞¶ʦҲnvrffbzzv涺Ʋbfbƾ¶rzv~~fjf~zƶvvrƺ~z~z~zzºvzvʾƾʺVFºv~~ºzzvjnjnrn~~~ƺƂz^jVrvvֺ'bKGDH pHYs  ~tIME(NhIDATxcHvd$`6$c3!C"9DbvT qNv?@d mlOX>q|>WG2)rUT2E_rRTDPƹh)sN05r f5zzLs' jYd:o~2CȺ7չlj"KvwuuK7mMbh7f^`;C -zJv2$7O61[`*;֕=)3MX (eiqZ'2nϵdm%|-Xcl3n#8xl#0խ[^kpqqBZc[vo .^5qiz=ծS{Ƕ<5[^!_xϏ05aDX:dtvvʁѡgl?VK^sju_ړO̧ ) Pcx H3{lltcޛvׯ({XO_Xs,Q -p r~Rxcd2&ؑ#wV&^~䅗>vzbS~L@0xqR'm(.)u3i 26轷/b/c&;{/N-ݯ:6cr<&8ۼ̌w܁Oʩزs7ciJ 8{;޳Jן:zǩ S_h0 MkG!s,ΐ-5=}QVޡa?DwOk:躆RպOR8y`o6sK!`r~?'gk^L iJmAdx|Q+5f/NPi&8C6f 9q, ?]:+o{Ɵ] ;lӟOYӏʦaP(QsƀendYFR] u@DmmJf %MSMeR0 bpqgw t~&*3Ksgk+Z~pϳO?=7F"f~n "Ҩ-P+_[?E}VO?He`+IZ'iażK#smm}qvBrU}m~yrs  iizV14bqDP_[#hQ,-X({3ˌ~Ҡ%T9ܲuD%(cR&om[.f^;y_1'72>'^+^ a`a`:)Hd-XtW:,QpYkXk_,͛,ͮQ;?OZ_FjX]"Fi[&id297C!PȻ  n탻ϭG~~|K,v(vddlMu(s86V/xyӴ)GT`bHD$Jul`vjKn*S,v-D$ ]"!q@6eBVAhY\^_:y33cթz}/vyFT)YCGɨ uh1-G(.eeZajA #A(f(Y,e#.Mdߡ;X]^5Hvo:Z 1 D4M1>.W^>pV揯>G|8N޵ik'ST@)z:(\ Kϰ0Ʊ B"s6<K0>İ)0Ǹ=BuRHUJi`ibzRHDocԱ{埪W'syqu|ߣT,bmut .]ıН,T2 N[-%MMkuQ/:ꑧ?Q,4XD*ꍀSz gm8J5RnjgN]6 €ARR IM(r&YC[)]L=7;a~RXh$\'t]CXE$-4r md26A!='/μo Ĺɏ#Z+dЋ!Zea2bJQg6$;ٳlHobA+kfN`r\< Z[4$"ɖI84%]?۪1-FH$ Q4 2m91=7pjɕ?F@EX,d[RrӭPo4M4bu$[w3@*aa)`rr8DJDx6gjzJnaaat ^N0$T!c4!MAD~;Ϩ hA:!EJ$"( lDul [G+~σ_{lahܳ]~$$U)B!A24k3֚G,\A,[cyaV# |t=[Yk8{+^Ɲ77x:ij`;I5ֈ$$Rw>%E75ĺO9aD,Nc:i5;gk\X]򌎒isGB2Y^k)IL;AO~4Cgnnp'#11bPOepa"kAdɕ8L;!1V/<@54]ÈlbDa\a땪AyߺypV@,%in6HS2Q$bz|Q*8FȐo5Mn&i?;1PqL D>KRZ)lV}9 -tR(RB>Mw;qCJ3iڭop5VdGX 8)V|2<:XqՒ  EH,ݶJSt][ou͕aeŐ&'5| /Xbf0 ]v!a(glfg癘D}yJ8 ,J?Bju' jCWX}ޥ7%-yЭ[WG/}D RC2"h!wD8SYoqz||빱mM[-\HYq4 4]. ӤP:&4ԅ:ܻЁıs/#EigdE*]#TMyۿ3U|t䖁?_mm?'+i4 !|1$I1y?j~_ ,Bd:c: TJ(44LˤYYedM~Lơ*Bx9覉a:"v͕lvl4u2`Wq;fGju]1/+9,2McXd,iĊ5Fͦ20jgf2f(I RR"emp[ ٜۃd }u榦01bAh'h,f|2N  )vU'\?S3'X[^0m22(pdKv7WчC-MSDAD$l4;o[i=~:?sϐ I䯡dL"ct ř3AR hyx!)Q )cD,Q#b$QT{FA&ɶϴ}^اi"BO-V߄Jty>8S1sߤќ"UX5mdqiiT* C0! B@BdBA 0" $"$$IBծ1*!B ]Iv^qZ%#TV%Ů"KyV5-yrg׬!EF4iD (=,$ I!"$R"EHw=jǮ OTru7_jܵ"EBPw}"j ġG.cK`kL4qxe+EHz$TXg~W:@Oju }#E?$%m?Pmc؎jc7\v2_8ғ\ ]REs37v^}\!`ˆowv1-DXG>йa-u?W-્+lx%W*ǨD qpg#B@6f要eUQ?Zƭ~mY a9l'KOv>C^+#I#J~3X3/>ܻc%7VF >ڙNONUvCw?T-?qEuqqbIENDB`./qjoypad-4.1.0/icons/gamepad1-64x64.png0000644000175000001440000001114511160766732016301 0ustar johnusersPNG  IHDR@@% bKGD pHYs  ~tIME IDATxZi[q=}۰QdQ"Jd[bJI9N%U|J$[eYNRD,  ۝3XEGCB͂۷9$"8 `sp_lT8NBh= >ݸvW{L;M"rZvT*54;5Dںx}|7삾 'Ng/)@L!]Bs@",H'V_~ ӈNn@O*?wޠO?IbS,xB!8E([Ck_[ml4ݷ-lPDo~^\~#L|Zyьs|~;8(po G; O?}FSiowUx?ی(<Χ#h-Wr_ޕWiqg8 x}5H-/|7+VHDp2QD@ab k1V,3X h/@{K~}=!>9]xVj,2pԋȤI|R*qvkAsW~t[7땠dhD 1~{`wE12ae@Kl1_Z dp8y?Xj,^^lyx 1,{imno$se*1IH "v0z;wo_gj3vfrR14zDt&y{+`@H!"E MfXD$JD6?=hT볳|Tj%6iqtN#!$&Ƶ!k!9I{/,AS)"@D~=ҥ ߨ7 RnE$gH>d:rzƵ^BX& 8j F# v\<%}Vi:&F㻷^Z, pJdFػ[62ll,hImѯ{;E7_|ik%K-!JcFȊ%FPy9ee^e!P4$;$Q4C"v]j[f+ZK >aX+ Hl5ݙR}q~ j*ף D4$ GW]#*k?UωVLq 2ԝ )%"0TՅR!W QD!'B)aO)aIݸ~zJSV*qzAc1R| rdDYp2 tBQ{jXZj,K@Phse`89M\$ Q6 9Vq8{W/ |\qo$2zFS?bpƠD$JR\,5)4rADX Y=wmvRM4Tͽ7^s،8徿`0yer)Q8Ĥ (r=/{7jkk˫aDq"/釓psWHFLP*nl ^~IkmYpc#(9w,vsq*AMvj3͝f\{sgVjt"D `K`۴wa= Ϭv'a[Bb9#!bC g% i@1 0єD1ĄQ37w4w.J1_'K*u[!d2olܝ7_x^X+J;PĆEX ^ udk8ΪlX÷oo !DhDL`@`üw8ČG_}V9dq7A7NSbV`4J ݫW4w_{k O7!b%W޿z;;$ \Z%˂ $Ki($[6؈( )@@ Es=R4c@"@ihǕ3gfgksq3 [0rYZ2[h( \E50oάO&^}RoQ>(S*T(âB)_4cY)4 @ R0__za\H͍^[F{{QC(yA+\wDINlQAw\^):<ͭ] ~X~ڔ6Dq a:MEtҗ/=?+<'??߹i 1̍Ƣ֎&$GEELunb)16U$a? Pe,IcFv<`X@‘컎>zvyaQڭ6L4I!-<D2Hq0V{g@Mxgkյէ>O33@C""8zƓd'u|73*(kfʍa(dTr2+VH 贎?~m@T2NwׂEQlA`Ebt9)3a XAfj$ywI fX˃PDut1_e 8QbIy5h㑣hcNac-(<9"X] bx2NC DKClbRDfP ~fU86sYD8IuIcM .+vVg]Ess~ww%LKR bXh s{ۊ(? L!V|]p$ q5 TpU)VYBJ)H A+(D{B8R}~>'~G$}1i,g*%xtzãp(fG jI'QZ1"@$5a5JH;wzfN6qi'q3;3S-孵I 3"0 %b$Z;lM aǣqW/,onl'1LDtZI|8Zd|>h7`9uL7t1(5Lz844$5QXrr 8'IXk,^Lӽ`2 z Za8nPJ~T.A'oɺtvKOUQʭ l-x<7"uzT;^CxAP*u:x*]](Ԍ4b@J C[c@:#eP/Q#ja.W%4TqV:tU ?;H 3'7Y&=Ѫ IZbaOpTq"QsqC&g_uگ|ehi`W9c1QސAS‰@ANb!(?^M_#BP^={knw̆HrNRMEPc\km'Օ %L iDn[ *?Z飫%ų NP4"r-:=n|d)cN`~Q7C$rrBR (䊋= أ1ۇo?W>Fz`8`+JN5v+, iG!iDjV#櫗Ccڸ"J2rRLYgd2b?ﮯ(29wBtn)!Ti?JWfܼe,D2\@h:ڛ XJ`iQ¥V]7/\zV-1,Yx*8EX60ZZ[Y[`ᰐ:~Od2w{g~+NLldv1ߪr\-K"Q?V`:Y'h;?;hmGQh. 84S+Vy:!fI-/~ب/{c/ҁljL+ 5e d LI$1"NTkof!_<8NŃ~Wnh0&NMbL"2@iVO_zWtv_l v46wvn3I gKg>R*_pɿ,bN5E'/؁㊬K>sό%j~W<~#'KdW-twP8TIENDB`./qjoypad-4.1.0/icons/gamepad3-24x24.png0000644000175000001440000000216511160766732016275 0ustar johnusersPNG  IHDRw=bKGD pHYs  ~tIME 66LIDATxke;Mvf$͡[Rj xPB)E[rxUo wQZSڴƦ6f;;;3^*URy~/yV # ;?u6V`me=lET/G85AW 6 |e:tQ9I^96?@~e4H29b.˥>@O ) w9kģQ*R Ip|Ab1a?ƦX}wS=|p"M8 L1b1Aja SQUɉ8xr |8ws/ĩs8rhH<,=3v"M/fշmDQ¯P|>,8Ϯ>Dl6&]E heD9@YPm44;WPKa3euX=C\ǃ]No'i u 뛄iBJ9BЯ[G4+TY:"51-_ݵǣܻuw=|Lf7+x'^cL+MZZ%2j~K\@ ]^V3.}kH<@J*$iz'\d"DplS]Op }'3{Vl!^XbBtj[w,@,ԑ,CѢg IR5;toԧXxLCghۘ!=lf_r Qut d,F~ukYiW,۶`x!(Jm Kn4wO9vyl|OLwgIENDB`./qjoypad-4.1.0/icons/gamepad4-24x24.png0000644000175000001440000000235611160766732016300 0ustar johnusersPNG  IHDRw=bKGD pHYs  ~tIME -{IDATxՔKl;ݙWlv -BHMH QL0D$'ODO&&BLBh"BYcJB[hϙٙnjcB_|q|r'dwf5ql-?iÍbCGS%Y\RuĮK_ȲڡS MC,CSWy}^xp-wWƅ' F ]Bp1!@VAX͹$葳:ȏӌƮ~n%ʒ NϢ(Y/BZͺvsE:#_׷m 4~~T~vYfs-u:."0 X5 J\n{=P5cx7g򿏃 X*_ Dw7f'P iy\(H٘_4M+(Z">xB,JQ¹3c@.P aeq0SԺmfmö,(EYZV7ə em9}LJX4L eS?ERQѲ芉JrU@J)(~=fE.HemfHR2QX6E!|2(fkuĒO#T# ʪw[7FGO~|y!v'ARwNf7帖et]2Xۍv"(C(( fy*pcr_[& VvQV加)"I*h6=xM+j(]3j'7Y`RHOKܛ1tLX]ܖMVQQj6>yuӗg+K ;s/0SHN]919=P UdyQ7l6aT޴20LBpZD?ovzMxjӏ'I^*õۋ%\.UZVڍ&Wvb 70d>z oPzzg Ocłr8rf]Yx~< \IENDB`./qjoypad-4.1.0/icons/gamepad2-24x24.png0000644000175000001440000000272111160766732016272 0ustar johnusersPNG  IHDRw=bKGD pHYs  d_tIME !!]^IDATxio;3]HmQSG$ql@,)~пH~i6H뺊-[K%Z(R\m8sQI039U<2L"?PIRy))6j;'Y߀V$@b "y*=/>)_니ԩ 5gq<hYzcw^3ZX fq "ƛo>n3._C(q6b[*XX¥K4Gt:>i7 avF,ثl@Y: H$#B\xWv2 `$Wl..^*wZHNT EW&N)yݜ8y#ɎIENDB`