pax_global_header00006660000000000000000000000064135067524620014524gustar00rootroot0000000000000052 comment=99f23f89296015783c7df71e14b516bd202deb0b qjoypad-4.3.1/000077500000000000000000000000001350675246200132005ustar00rootroot00000000000000qjoypad-4.3.1/.gitignore000066400000000000000000000000451350675246200151670ustar00rootroot00000000000000.* build build-* CMakeLists.txt.user qjoypad-4.3.1/CMakeLists.txt000066400000000000000000000051731350675246200157460ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.11) project(qjoypad) set(QJOYPAD_MAJOR 4) set(QJOYPAD_MINOR 3) set(QJOYPAD_PATCH 0) find_package(Qt5Widgets REQUIRED) find_package(Qt5LinguistTools REQUIRED) find_package(Qt5X11Extras REQUIRED) option(WITH_LIBUDEV "Use libudev for automatically updating joypad devices." ON) if(WITH_LIBUDEV) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBUDEV libudev) if(NOT(LIBUDEV_FOUND)) message(FATAL_ERROR "libudev not found. If you don't want to compile with libudev support use -DWITH_LIBUDEV=OFF") endif() link_directories(${LIBUDEV_LIBRARY_DIRS}) include_directories(${LIBUDEV_INCLUDE_DIRS}) endif() set(DEVICE_DIR "/dev/input" CACHE PATH "Set the path where QJoyPad will look for your joystick devices. If your devices are /dev/js0, /dev/js1, etc., this should be just \"/dev\". By default, this is /dev/input.") option(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." OFF) option(UPDATE_TRANSLATIONS "Update source translation locale/*.ts files (WARNING: make clean will delete the source .ts files! Danger!)") message(STATUS "Using device directory: ${DEVICE_DIR}") if(PLAIN_KEYS) message(STATUS "Using regular XWindows key names.") add_definitions(-DPLAIN_KEYS) endif() if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -pedantic -Wno-variadic-macros") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") endif() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") # for config.h include_directories("${PROJECT_BINARY_DIR}/src") file(GLOB qjoypad_TRANS_SOURCES translations/qjoypad*.ts) if(UPDATE_TRANSLATIONS) file(GLOB_RECURSE qjoypad_TRANS_FILES *.cpp *.h) qt5_create_translation(qjoypad_TRANS ${qjoypad_TRANS_SOURCES} ${qjoypad_TRANS_FILES}) else() qt5_add_translation(qjoypad_TRANS ${qjoypad_TRANS_SOURCES}) endif() add_subdirectory(icons) add_subdirectory(src) add_custom_target(translations_target DEPENDS ${qjoypad_TRANS}) add_dependencies(qjoypad translations_target) install(PROGRAMS qjoypad.desktop DESTINATION "share/applications") install(FILES ${qjoypad_TRANS} DESTINATION "share/qjoypad/translations") # uninstall target configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake") qjoypad-4.3.1/INSTALL.txt000066400000000000000000000003331350675246200150460ustar00rootroot00000000000000For a quick install, just follow these steps: git clone https://github.com/panzi/qjoypad mkdir qjoypad/build cd qjoypad/build cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release make -j`nproc` make install qjoypad-4.3.1/LICENSE.txt000066400000000000000000000431311350675246200150250ustar00rootroot00000000000000 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.3.1/README.md000066400000000000000000001017741350675246200144710ustar00rootroot00000000000000QJoyPad 4 ========= This is a fork of [QJoyPad](http://qjoypad.sourceforge.net/) with some small additional features, Qt 5 port and some bug/memory leak fixes. QJoyPad was originally developed by Nathan Gaylinn and John Toman . This fork is maintained by Mathias Panzenböck . ![Screenshot](http://i.imgur.com/Cuql4Mr.png) Quick Installation ------------------ git clone https://github.com/panzi/qjoypad mkdir qjoypad/build cd qjoypad/build cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release make -j`nproc` make install For more details see [Installation](#installation). Documentation ------------- ### Introduction #### 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. #### 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. #### 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! - If libudev is availabe the joypad list will automatically udpated. ### Getting Started #### Requirements - A Linux computer and a Linux-compatible gaming device - A Linux kernel with joystick support (see the [Linux Kernel](https://www.kernel.org/) and the Linux joystick driver documentation [joystick.txt](https://www.kernel.org/doc/Documentation/input/joystick.txt) and [joystick-parport.txt](https://www.kernel.org/doc/Documentation/input/joystick-parport.txt)) - [XWindows](http://x.org/) - [Qt 5](http://qt-project.org/) - [libudev](http://www.freedesktop.org/software/systemd/libudev/) (optional) #### Installation Installing QJoyPad should be a quick and painless process. The basic procedure is: git clone https://github.com/panzi/qjoypad mkdir qjoypad/build cd qjoypad/build cmake .. -DCMAKE_BUILD_TYPE=Release make -j`nproc` 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 cmake like this: `cmake .. -DDEVICE_DIR=/dev` Do this if your joystick devices are `/dev/js0`, `/dev/js1` etc. 2. Install prefix: The default install prefix is `/usr`. To change this invoke cmake like this: `cmake .. -DCMAKE_INSTALL_PREFIX=/usr` 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 would rather like the original X11 key names you can invoke cmake like this: `cmake .. -DPLAIN_KEYS=ON` 4. Disable libudev support: If you don't have libudev you can disable libudev support like this: `cmake .. -DWITH_LIBUDEV=OFF` ### Using QJoyPad #### The Tray Icon ![Tray Icon and Popup Menu](http://i.imgur.com/8kc0CQ6.png) QJoyPad 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 [But my window manager doesn't HAVE a system tray!](#but-my-window-manager-doesnt-have-a-system-tray)). 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. #### 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 [Joystick recognition](#joystick-recognition) 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. #### The Setup Dialog ![Setup Dialog](http://i.imgur.com/f3TxINW.png) The following sections describe the parts of the Setup Dialog going from the top down and from left to right. ##### 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. ##### 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 [Layout Files](#layout_files) for details). #### The Remove button The Remove button deletes the layout currently selected in the combo box, losing it forever. #### The Save button The Save 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 Save to save any changes you make before quitting or switching to another layout, or all the changes will be forgotten! #### The Revert button The Revert button does about the opposite of Save. If you've gone and changed the keys around, pressing Revert will return them to how they are saved in the current layout. #### The Joystick buttons Immediately below the Add, Remove, Save, and Revert buttons, there are several buttons labeled like "Joystick 1 (Microsoft X-Box 360 pad)", "Joystick 2 (Nintendo Wiimote)", 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 to let you know which it is. #### 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. #### 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. #### 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! ### Configuring axes ![Axis Edit](http://i.imgur.com/CPcyuq2.png) 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. #### 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 [Joystick adjustment](#joystick-adjustment)). #### 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. #### 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. #### 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. #### 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). #### 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. ### Configuring buttons ![BUtton Edit](http://i.imgur.com/Grwrunu.png) 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. #### 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. #### 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. #### 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. ### 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 [Layout Files](#layout-files)). 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. ## 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 '/' and dependig on your file system '\*', 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. ## Problems ### 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 [joystick.txt](https://www.kernel.org/doc/Documentation/input/joystick.txt) or [joystick-parport.txt](https://www.kernel.org/doc/Documentation/input/joystick-parport.txt); 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), although this document seems to be outdated) 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 (Nathan Gaylinn's) 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 [Joystick recognition](#joystick-recognition) for some tips for checking if your joystick is working. ### Joystick recognition #### QJoyPad says it can't find any joysticks? #### QJoyPad isn't showing all of my joysticks. #### 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 [Installation](#installation)); 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. Or you can install the jstest command line tool and use `jstest /dev/input/js0`, which will give you a much nicer output. 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 QJoyPad 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 and think it's QJoyPad's fault please [write a bug report](https://github.com/panzi/qjoypad/issues). ### Joystick adjustment #### Why does it say I'm moving if I'm not? #### I keep going in two directions at once instead of just one! #### 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 or with the KDE system settings "Input Devices" module. ### 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 /tmp/qjoypad.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. ### 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 or newer 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 `/tmp`. 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. ### 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. ### 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](https://sourceforge.net/projects/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. Another alternative is [Jojsticken](http://jojsticken.sourceforge.net/). (See the links section there to find even more alternatives.) Finally there is [AntiMicro](https://github.com/AntiMicro/antimicro), which seems to have even more features and supported platforms than QJoyPad. ### 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. ### I hate the QJoyPad icon. Is there any way to change it? Absolutely! Starting with version 4.2 QJoyPad stores its icons at these locations: $INSTALL_PREFIX/share/icons/hicolor/24x24/apps/qjoypad.png $INSTALL_PREFIX/share/icons/hicolor/64x64/apps/qjoypad.png (Where `$INSTALL_PREFIX` is usually `/usr` or `/usr/local`.) Just replace these files and restart QJoyPad. ### Why do I have to tell QJoyPad to "update joystick devices"? Why can't it do that on its own? If you compile with `-DWITH_LIBUDEV=ON` (the default) then UDev is used to automatically update the joypad list. If automatically updating of the joypad list still does not work compile with `-DCMAKE_BUILD_TYPE=Debug` and post the output on the [GitHub bug tracker](https://github.com/panzi/qjoypad/issues). You can force QJoyPad to rescan your joypads at any time using the menu or by running `qjoypad --update`. ### 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 use a X-Box 360 pad and teh LEDs keep blinking try to re-plug it. If you keep having troubles, see [Joystick recognition](#joystick-recognition). ### 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; or in math for that matter) 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. ### 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 (and newer). 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. You can find old versions of QJoyPad [here](https://sourceforge.net/projects/qjoypad/files/qjoypad/). However, they probably won't work on modern Linux distributions because they use obsolete Qt versions. ### Features and suggestions #### Why can't I click with an axis, or move the mouse with a #### 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 you might want to [write a feature request](https://github.com/panzi/qjoypad/issues) on github. ## Credits Thank you to [Erich Kitzmüller](http://members.chello.at/erich.kitzmueller/ammoq/main.html), author of xjoypad for the inspiration to write QJoyPad and for the code that started Nathan Gaylinn 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. (Note from Mathias Panzenböck: I guess this is about the tray icon code before Qt 4. Qt 4 and newer has nice support for the tray.) 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. ## Licensing This software is licensed under the terms of the GNU GPLv2. Please see LICENSE.txt for a full text of the license. qjoypad-4.3.1/cmake/000077500000000000000000000000001350675246200142605ustar00rootroot00000000000000qjoypad-4.3.1/cmake/cmake_uninstall.cmake.in000066400000000000000000000013741350675246200210450ustar00rootroot00000000000000if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif() file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach (file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") if (EXISTS "$ENV{DESTDIR}${file}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT ${rm_retval} EQUAL 0) message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") endif () else () message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") endif () endforeach() qjoypad-4.3.1/icons/000077500000000000000000000000001350675246200143135ustar00rootroot00000000000000qjoypad-4.3.1/icons/CMakeLists.txt000066400000000000000000000003041350675246200170500ustar00rootroot00000000000000install(FILES gamepad4-24x24.png DESTINATION "share/icons/hicolor/24x24/apps" RENAME qjoypad.png) install(FILES gamepad4-64x64.png DESTINATION "share/icons/hicolor/64x64/apps" RENAME qjoypad.png) qjoypad-4.3.1/icons/gamepad1-24x24.png000066400000000000000000000020261350675246200172610ustar00rootroot00000000000000PNG  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%|-Xclݸ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.3.1/icons/gamepad2-24x24.png000066400000000000000000000027211350675246200172640ustar00rootroot00000000000000PNG  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`qjoypad-4.3.1/icons/gamepad2-64x64.png000066400000000000000000000150771350675246200173040ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs  ~tIME % eIDATxśidǕgyY<- W hÂ,>X7Cƍ&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.3.1/icons/gamepad3-24x24.png000066400000000000000000000021651350675246200172670ustar00rootroot00000000000000PNG  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.3.1/icons/gamepad3-64x64.png000066400000000000000000000121541350675246200172760ustar00rootroot00000000000000PNG  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خ9rmxB,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.3.1/icons/gamepad4-64x64.png000066400000000000000000000124401350675246200172750ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs  ~tIME ,2N IDATxk]u}﹯-[02I\N%$v%LqUSfj!CƎgR.Ǐ0l$^VK}>3n#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.3.1/qjoypad.desktop000077500000000000000000000010321350675246200162410ustar00rootroot00000000000000[Desktop Entry] Name=QJoyPad GenericName=Joypad to Keyboard/Mouse Mapper GenericName[de]=Joypad zu Tastatur/Maus Mapper GenericName[fr]=Contrôle du clavier/souris avec un joystick Exec=qjoypad Icon=qjoypad Type=Application Terminal=false Categories=Qt;Game; Comment=Maps joypad button and stick events to keyboard and mouse events. Comment[de]=Bildet Joypad Knopf und Stick Ereignisse auf Tastatur und Maus Ereignisse ab. Comment[fr]=Associe les boutons et sticks directionnels d'un joystick à des événements de clavier et de souris. qjoypad-4.3.1/src/000077500000000000000000000000001350675246200137675ustar00rootroot00000000000000qjoypad-4.3.1/src/CMakeLists.txt000066400000000000000000000015051350675246200165300ustar00rootroot00000000000000include(GenerateExportHeader) configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) set(qjoypad_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 keydialog.cpp layout.cpp layout_edit.cpp main.cpp quickset.cpp) set(qjoypad_QOBJECT_HEADERS axis_edit.h axis.h axisw.h button_edit.h button.h buttonw.h flash.h icon.h joypad.h joypadw.h joyslider.h keycode.h keydialog.hpp layout_edit.h layout.h quickset.h) qt5_wrap_cpp(qjoypad_HEADERS_MOC ${qjoypad_QOBJECT_HEADERS}) add_executable(qjoypad ${qjoypad_SOURCES} ${qjoypad_HEADERS_MOC}) target_link_libraries(qjoypad Qt5::Widgets Qt5::X11Extras Xtst X11 ${LIBUDEV_LIBRARIES}) install(TARGETS qjoypad RUNTIME DESTINATION "bin") qjoypad-4.3.1/src/axis.cpp000066400000000000000000000273071350675246200154500ustar00rootroot00000000000000#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; } Axis::~Axis() { release(); } 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 <= PowerFunction) 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; } else if (*it == "+mouse") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= MAXKEY) { puseMouse = true; pkeycode = val; } else return false; } else if (*it == "-mouse") { ++it; if (it == words.end()) return false; val = (*it).toInt(&ok); if (ok && val >= 0 && val <= MAXKEY) { nuseMouse = true; 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 = MousePosVert; } else if (*it == "mouse-v") { mode = MouseNegVert; } else if (*it == "mouse+h") { mode = MousePosHor; } else if (*it == "mouse-h") { mode = MouseNegHor; } //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 << "\tAxis " << (index+1) << ": "; 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 == Keyboard) { stream << (puseMouse ? "+mouse " : "+key ") << pkeycode << ", " << (nuseMouse ? "-mouse " : "-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 == MousePosVert) stream << "+v\n"; else if (mode == MouseNegVert) stream << "-v\n"; else if (mode == MousePosHor) stream << "+h\n"; else if (mode == MouseNegHor) 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 = Keyboard; pkeycode = 0; nkeycode = 0; puseMouse = false; nuseMouse = false; downkey = 0; state = 0; adjustGradient(); } bool Axis::isDefault() { return (gradient == false) && (throttle == 0) && (maxSpeed == 100) && (dZone == DZONE) && (xZone == XZONE) && (mode == Keyboard) && (pkeycode == 0) && (nkeycode == 0) && (puseMouse == false) && (nuseMouse == false) ; } QString Axis::getName() { return tr("Axis %1").arg(index+1); } 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 label; if (mode == Keyboard) { if (throttle == 0) { if (puseMouse != nuseMouse) { label = tr("KEYBOARD/MOUSE"); } else if (puseMouse) { label = tr("MOUSE"); } else { label = tr("KEYBOARD"); } } else { label = tr("THROTTLE"); } } else { label = tr("MOUSE"); } return QString("%1 : [%2]").arg(getName(), label); } void Axis::setKey(bool positive, int value) { setKey(false, positive, value); } void Axis::setKey(bool useMouse, bool positive, int value) { if (positive) { pkeycode = value; puseMouse = useMouse; } else { nkeycode = value; nuseMouse = useMouse; } } void Axis::timerTick( int tick ) { if (isOn) { if (mode == Keyboard) { 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 ) { FakeEvent e; if (mode == Keyboard) { //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; bool useMouse = (state > 0)?puseMouse:nuseMouse; if (press) { e.type = useMouse ? FakeEvent::MouseDown : FakeEvent::KeyDown; downkey = (state > 0)?pkeycode:nkeycode; } else { e.type = useMouse ? FakeEvent::MouseUp : FakeEvent::KeyUp; } e.keycode = downkey; } //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 QuadraticExtreme: fdist = sqr(u); if(u >= 0.95F) { fdist *= 1.5F; } break; case PowerFunction: 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 = FakeEvent::MouseMove; if (mode == MousePosVert) { e.move.x = 0; e.move.y = dist; } else if (mode == MouseNegVert) { e.move.x = 0; e.move.y = -dist; } else if (mode == MousePosHor) { e.move.x = dist; e.move.y = 0; } else if (mode == MouseNegHor) { e.move.x = -dist; e.move.y = 0; } } //actually create the event sendevent(e); } qjoypad-4.3.1/src/axis.h000066400000000000000000000062601350675246200151100ustar00rootroot00000000000000#ifndef QJOYPAD_AXIS_H #define QJOYPAD_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 //represents one joystick axis class Axis : public QObject { Q_OBJECT //each axis can create a key press or move the mouse in one of four directions. enum Mode {Keyboard, MousePosVert, MouseNegVert, MousePosHor, MouseNegHor}; enum TransferCurve {Linear, Quadratic, Cubic, QuadraticExtreme, PowerFunction}; //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(); //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); void setKey(bool useMouse, 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(); int axisIndex() const { return index; } 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; //the index of this axis on the joystick 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; Mode mode; //positive keycode int pkeycode; //negative keycode int nkeycode; bool puseMouse; bool nuseMouse; //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.3.1/src/axis_edit.cpp000066400000000000000000000153301350675246200164460ustar00rootroot00000000000000#include "config.h" #include "axis_edit.h" AxisEdit::AxisEdit( Axis* ax ) :QDialog() { //build the dialog, display current axis settings :) axis = ax; setWindowTitle("Set " + axis->getName()); setWindowIcon(QPixmap(QJOYPAD_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); chkGradient = new QCheckBox(tr("&Gradient"), this); chkGradient->setChecked(axis->gradient); connect(chkGradient, SIGNAL(toggled(bool)), this, SLOT( gradientChanged( bool ))); v2->addWidget(chkGradient); cmbMode = new QComboBox(this); cmbMode->insertItem((int) Axis::Keyboard, tr("Keyboard/Mouse Button"), Qt::DisplayRole); cmbMode->insertItem((int) Axis::MousePosVert, tr("Mouse (Vert.)"),Qt::DisplayRole); cmbMode->insertItem((int) Axis::MouseNegVert, tr("Mouse (Vert. Rev.)"), Qt::DisplayRole); cmbMode->insertItem((int) Axis::MousePosHor, tr("Mouse (Hor.)"), Qt::DisplayRole); cmbMode->insertItem((int) Axis::MouseNegHor, tr("Mouse (Hor. Rev.)"), Qt::DisplayRole); cmbMode->setCurrentIndex( axis->mode ); connect(cmbMode, SIGNAL(activated(int)), this, SLOT( modeChanged( int ))); v2->addWidget(cmbMode); cmbTransferCurve = new QComboBox(this); cmbTransferCurve->insertItem(Axis::Linear, tr("Linear"), Qt::DisplayRole); cmbTransferCurve->insertItem(Axis::Quadratic, tr("Quadratic"), Qt::DisplayRole ); cmbTransferCurve->insertItem(Axis::Cubic, tr("Cubic"), Qt::DisplayRole ); cmbTransferCurve->insertItem(Axis::QuadraticExtreme, tr("Quadratic Extreme"), Qt::DisplayRole); cmbTransferCurve->insertItem(Axis::PowerFunction, tr("Power Function"), Qt::DisplayRole); cmbTransferCurve->setCurrentIndex( axis->transferCurve ); cmbTransferCurve->setEnabled(axis->gradient); connect(cmbTransferCurve, SIGNAL(activated(int)), this, SLOT( transferCurveChanged( int ))); v2->addWidget(cmbTransferCurve); 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(tr("&Mouse Speed"), mouseBox); v2->addWidget(mouseLabel); spinSpeed = new QSpinBox(mouseBox); spinSpeed->setRange(0,MAXMOUSESPEED); spinSpeed->setSingleStep(1); spinSpeed->setValue(axis->maxSpeed); v2->addWidget(spinSpeed); lblSensitivity = new QLabel(tr("&Sensitivity"), mouseBox); v2->addWidget(lblSensitivity); spinSensitivity = new QDoubleSpinBox(mouseBox); spinSensitivity->setRange(1e-3F, 1e+3F); spinSensitivity->setSingleStep(0.10); spinSensitivity->setValue(axis->sensitivity); v2->addWidget(spinSensitivity); h->addWidget(mouseBox); mouseLabel->setBuddy(spinSpeed); lblSensitivity->setBuddy(spinSensitivity); 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); btnNeg = new KeyButton(axis->getName(),axis->nkeycode,keyBox,true,axis->nuseMouse); cmbThrottle = new QComboBox(keyBox); cmbThrottle->insertItem(0, tr("Neg. Throttle"), Qt::DisplayRole); cmbThrottle->insertItem(1, tr("No Throttle"), Qt::DisplayRole); cmbThrottle->insertItem(2, tr("Pos. Throttle"), Qt::DisplayRole); cmbThrottle->setCurrentIndex(axis->throttle + 1); connect( cmbThrottle, SIGNAL( activated( int )), this, SLOT( throttleChanged( int ))); btnPos = new KeyButton(axis->getName(),axis->pkeycode,keyBox,true,axis->puseMouse); h->addWidget(btnNeg); h->addWidget(cmbThrottle); h->addWidget(btnPos); v->addWidget( keyBox ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); v->addWidget(buttonBox); modeChanged( axis->mode ); transferCurveChanged( axis->transferCurve ); throttleChanged( axis->throttle + 1 ); } void AxisEdit::show() { QDialog::show(); setFixedSize(size()); } void AxisEdit::setState( int val ) { slider->setValue( val ); } void AxisEdit::gradientChanged( bool on ) { cmbTransferCurve->setEnabled(on); if (on) { transferCurveChanged( axis->transferCurve ); } else { lblSensitivity->setEnabled(false); spinSensitivity->setEnabled(false); } } void AxisEdit::modeChanged( int index ) { if (index == Axis::Keyboard) { mouseBox->setEnabled(false); keyBox->setEnabled(true); } else { mouseBox->setEnabled(true); keyBox->setEnabled(false); if (chkGradient->isChecked()) { cmbTransferCurve->setEnabled(true); transferCurveChanged( axis->transferCurve ); } } } void AxisEdit::transferCurveChanged( int index ) { if (index == Axis::PowerFunction) { lblSensitivity->setEnabled(true); spinSensitivity->setEnabled(true); } else { lblSensitivity->setEnabled(false); spinSensitivity->setEnabled(false); } } void AxisEdit::throttleChanged( int index ) { switch (index) { case 0: btnNeg->setEnabled(true); btnPos->setEnabled(false); break; case 1: btnNeg->setEnabled(true); btnPos->setEnabled(true); break; case 2: btnNeg->setEnabled(false); btnPos->setEnabled(true); break; } slider->setThrottle( index - 1 ); } void AxisEdit::accept() { axis->gradient = chkGradient->isChecked(); axis->maxSpeed = spinSpeed->value(); axis->transferCurve = (Axis::TransferCurve)cmbTransferCurve->currentIndex(); axis->sensitivity = spinSensitivity->value(); axis->throttle = cmbThrottle->currentIndex() - 1; axis->dZone = slider->deadZone(); axis->xZone = slider->xZone(); axis->mode = (Axis::Mode) cmbMode->currentIndex(); axis->pkeycode = btnPos->getValue(); axis->nkeycode = btnNeg->getValue(); axis->puseMouse = btnPos->choseMouse(); axis->nuseMouse = btnNeg->choseMouse(); axis->adjustGradient(); QDialog::accept(); } qjoypad-4.3.1/src/axis_edit.h000066400000000000000000000022521350675246200161120ustar00rootroot00000000000000#ifndef QJOYPAD_AXIS_EDIT_H #define QJOYPAD_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 #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 gradientChanged( bool on ); void modeChanged( int index ); void transferCurveChanged( int index ); void throttleChanged( int index ); void accept(); protected: //the associated Axis that needs to be set. Axis *axis; //the important parts of the dialog: QCheckBox *chkGradient; QComboBox *cmbMode, *cmbThrottle, *cmbTransferCurve; QFrame *mouseBox, *keyBox; QSpinBox *spinSpeed; QLabel *lblSensitivity; QDoubleSpinBox *spinSensitivity; KeyButton *btnNeg, *btnPos; JoySlider *slider; }; #endif qjoypad-4.3.1/src/axisw.cpp000066400000000000000000000015771350675246200156400ustar00rootroot00000000000000#include "axisw.h" AxisWidget::AxisWidget( Axis* a, QWidget* parent ) : FlashButton(QString(), QString(), 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.3.1/src/axisw.h000066400000000000000000000015121350675246200152720ustar00rootroot00000000000000#ifndef QJOYPAD_AXIS_WIDGET_H #define QJOYPAD_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 { Q_OBJECT 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.3.1/src/button.cpp000066400000000000000000000106671350675246200160200ustar00rootroot00000000000000#include "button.h" #include "event.h" Button::Button( int i, QObject *parent ) : QObject(parent) { index = i; isButtonPressed = false; isDown = false; rapidfire = false; toDefault(); tick = 0; } Button::~Button() { release(); } 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; keycode = 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 << "\tButton " << (index+1) << ": "; if (rapidfire) stream << "rapidfire, "; if (sticky) stream << "sticky, "; stream << (useMouse ? "mouse " : "key ") << 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; timer.stop(); } bool Button::isDefault() { return (rapidfire == false) && (sticky == false) && (useMouse == false) && (keycode == 0); } QString Button::getName() { return tr("Button %1").arg(index+1); } QString Button::status() { if (useMouse) { return tr("%1 : Mouse %2").arg(getName()).arg(keycode); } else { return tr("%1 : %2").arg(getName(), ktos(keycode)); } } void Button::setKey( bool mouse, int value ) { useMouse = mouse; keycode = value; } 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; FakeEvent click; //determine which of the four possible events we're sending. if (press) click.type = useMouse ? FakeEvent::MouseDown : FakeEvent::KeyDown; else click.type = useMouse ? FakeEvent::MouseUp : FakeEvent::KeyUp; //set up the event, click.keycode = keycode; //and send it. sendevent(click); } void Button::timerCalled() { timerTick(++tick); } qjoypad-4.3.1/src/button.h000066400000000000000000000032201350675246200154500ustar00rootroot00000000000000#ifndef QJOYPAD_BUTTON_H #define QJOYPAD_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(); //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 ); int buttonIndex() const { return index; } 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; QTimer timer; public slots: void timerCalled(); }; #endif qjoypad-4.3.1/src/button_edit.cpp000066400000000000000000000032661350675246200170220ustar00rootroot00000000000000#include "config.h" #include "button_edit.h" #include #include ButtonEdit::ButtonEdit(Button* butt) : QDialog(0) { setModal(true); //build the dialog! button = butt; setWindowTitle(tr("Set %1").arg(button->getName())); setWindowIcon(QPixmap(QJOYPAD_ICON24)); QVBoxLayout* v = new QVBoxLayout(this); v->setMargin(5); v->setSpacing(5); btnKey = new KeyButton( button->getName(), button->keycode, this, true, button->useMouse); v->addWidget(btnKey); QHBoxLayout* h = new QHBoxLayout(); chkSticky = new QCheckBox(tr("&Sticky"), this); chkSticky->setChecked(button->sticky); h->addWidget(chkSticky); chkRapid = new QCheckBox(tr("&Rapid Fire"), this); chkRapid->setChecked(button->rapidfire); h->addWidget(chkRapid); v->addLayout(h); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); v->addWidget(buttonBox); } 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 = chkRapid->isChecked(); button->sticky = chkSticky->isChecked(); //if the user chose a mouse button... button->useMouse = btnKey->choseMouse(); button->keycode = btnKey->getValue(); QDialog::accept(); } qjoypad-4.3.1/src/button_edit.h000066400000000000000000000007271350675246200164660ustar00rootroot00000000000000#ifndef QJOYPAD_BUTTON_EDIT_H #define QJOYPAD_BUTTON_EDIT_H #include #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 *btnKey; QCheckBox *chkSticky, *chkRapid; }; #endif qjoypad-4.3.1/src/buttonw.cpp000066400000000000000000000010661350675246200162000ustar00rootroot00000000000000#include "buttonw.h" ButtonWidget::ButtonWidget( Button* b, QWidget* parent ) : FlashButton(QString(), QString(), parent), on(false), button(b) { update(); } 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.3.1/src/buttonw.h000066400000000000000000000011231350675246200156370ustar00rootroot00000000000000#ifndef QJOYPAD_BUTTON_WIDGET_H #define QJOYPAD_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 { Q_OBJECT 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.3.1/src/config.h.in000066400000000000000000000011421350675246200160100ustar00rootroot00000000000000#ifndef QJOYPAD_CONFIG_H_IN #define QJOYPAD_CONFIG_H_IN #cmakedefine QJOYPAD_MAJOR #cmakedefine QJOYPAD_MINOR #cmakedefine QJOYPAD_PATCH #define QJOYPAD_VERSION "@QJOYPAD_MAJOR@.@QJOYPAD_MINOR@.@QJOYPAD_PATCH@" #define QJOYPAD_NAME "QJoyPad @QJOYPAD_MAJOR@.@QJOYPAD_MINOR@" #define QJOYPAD_DEVDIR "@DEVICE_DIR@" #define QJOYPAD_ICON24 "@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/24x24/apps/qjoypad.png" #define QJOYPAD_ICON64 "@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/64x64/apps/qjoypad.png" #define QJOYPAD_L10N_DIR "@CMAKE_INSTALL_PREFIX@/share/qjoypad/translations/" #cmakedefine WITH_LIBUDEV #endif qjoypad-4.3.1/src/constant.h000066400000000000000000000012101350675246200157630ustar00rootroot00000000000000#ifndef QJOYPAD_CONSTANT_H #define QJOYPAD_CONSTANT_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 255 //fastest the mouse can go. Completely arbitrary. #define MAXMOUSESPEED 5000 #define SENSITIVITY_MIN 1e-8F #define SENSITIVITY_MAX 1e+8F #endif qjoypad-4.3.1/src/error.h000066400000000000000000000013151350675246200152710ustar00rootroot00000000000000#ifndef QJOYPAD_ERROR_H #define QJOYPAD_ERROR_H #include #include #include "config.h" //a nice simple way of throwing up an error message if something goes wrong. inline void errorBox(const QString &title, const QString &message, QWidget *parent = 0) { QMessageBox::warning(parent, QString("%1 - %2").arg(title, QJOYPAD_NAME), message, QMessageBox::Ok, Qt::NoButton); } inline void debug_mesg(const char *fmt, ...) __attribute__((format(printf,1,2))); #ifdef _DEBUG inline void debug_mesg(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } #else inline void debug_mesg(...) {} #define debug_mesg(...) {} #endif #endif qjoypad-4.3.1/src/event.cpp000066400000000000000000000016521350675246200156200ustar00rootroot00000000000000#include #include "event.h" //actually creates an XWindows event :) void sendevent(const FakeEvent &e) { Display* display = QX11Info::display(); switch (e.type) { case FakeEvent::MouseMove: if (e.move.x == 0 && e.move.y == 0) return; XTestFakeRelativeMotionEvent(display, e.move.x, e.move.y, 0); break; case FakeEvent::KeyUp: if (e.keycode == 0) return; XTestFakeKeyEvent(display, e.keycode, false, 0); break; case FakeEvent::KeyDown: if (e.keycode == 0) return; XTestFakeKeyEvent(display, e.keycode, true, 0); break; case FakeEvent::MouseUp: if (e.keycode == 0) return; XTestFakeButtonEvent(display, e.keycode, false, 0); break; case FakeEvent::MouseDown: if (e.keycode == 0) return; XTestFakeButtonEvent(display, e.keycode, true, 0); break; } XFlush(display); } qjoypad-4.3.1/src/event.h000066400000000000000000000011361350675246200152620ustar00rootroot00000000000000#ifndef QJOYPAD_EVENT_H #define QJOYPAD_EVENT_H //for the functions we need to generate keypresses / mouse actions #include //a simplified event structure that can handle buttons and mouse movements struct FakeEvent { //types of events QJoyPad can create. //KeyRelease, KeyPress, ButtonRelease, ButtonPress, and MouseMove enum EventType {KeyUp, KeyDown, MouseUp, MouseDown, MouseMove}; EventType type; union { int keycode; struct { int x; int y; } move; }; }; void sendevent(const FakeEvent& e); #endif qjoypad-4.3.1/src/flash.cpp000066400000000000000000000050501350675246200155700ustar00rootroot00000000000000#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(const QString &text, const QString &name, QWidget* parent ) : QPushButton( text, parent ) { this->setObjectName(name); //record the base palette for posterity. flashPalette = normalPalette = palette(); //define the palette the button will have when it flashes. flashPalette.setCurrentColorGroup(QPalette::Inactive); flashPalette.setColor(QPalette::Button, flashPalette.color(QPalette::Highlight)); flashPalette.setColor(QPalette::ButtonText, flashPalette.color(QPalette::HighlightedText)); flashing = false; setAutoDefault( false ); setFocusPolicy(Qt::NoFocus); } void FlashButton::flash() { emit( flashed( !flashing ) ); if (flashing) { setPalette( normalPalette ); flashing = false; } else { setPalette( flashPalette ); flashing = true; } } FlashRadioArray::FlashRadioArray( const QStringList &names, bool horizontal, QWidget* parent) : QWidget( parent ) { if (horizontal) { mainLayout = new QHBoxLayout( this); mainLayout->setMargin(5); mainLayout->setSpacing(5); } else { mainLayout = new QVBoxLayout( this); mainLayout->setMargin(5); mainLayout->setSpacing(5); } foreach (const QString &name, names) { FlashButton *button = new FlashButton( name, QString(), this ); buttons.append(button); //when any of the buttons is clicked, it calls the same function on this. connect( button, SIGNAL( clicked() ), this, SLOT( clicked() )); mainLayout->addWidget(button); } state = 0; if (!buttons.isEmpty()) { buttons[0]->setDown( true ); } } int FlashRadioArray::getState() { return state; } void FlashRadioArray::flash( int index ) { if (index < buttons.size()) { 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. int i = 0; foreach (FlashButton *button, buttons) { if ( button != sender() ) { button->setDown( false ); } else { state = i; button->setDown( true ); } ++ i; } emit changed( state ); } qjoypad-4.3.1/src/flash.h000066400000000000000000000032031350675246200152330ustar00rootroot00000000000000#ifndef QJOYPAD_FLASH_H #define QJOYPAD_FLASH_H #include #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(const QString& text, const QString& name = "" , QWidget* parent = 0); 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 flashing; //the normal, unflashed palette QPalette normalPalette; //the colorful flashed palette QPalette flashPalette; }; //An array of flashing mutually-exclusive toggle buttons. class FlashRadioArray : public QWidget { Q_OBJECT public: FlashRadioArray( const QStringList &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: //which is currently down int state; //the array of buttons QList buttons; //the main layout. QBoxLayout* mainLayout; }; #endif qjoypad-4.3.1/src/icon.cpp000066400000000000000000000017041350675246200154250ustar00rootroot00000000000000#include #include "icon.h" #include "config.h" FloatingIcon::FloatingIcon( const QString &icon, QMenu *popup, QWidget *parent, const char *name) : QDialog( parent ), icon(icon) { this->setObjectName(name); setAttribute(Qt::WA_QuitOnClose); setAttribute(Qt::WA_TranslucentBackground); setWindowFlags(Qt::FramelessWindowHint); setWindowTitle(tr("%1 Floating Icon").arg(QJOYPAD_NAME)); pop = popup; setFixedSize(this->icon.width(),this->icon.height()); } void FloatingIcon::mousePressEvent( QMouseEvent* event ) { //if it was the right mouse button, if (event->button() == Qt::RightButton) { //bring up the popup menu. pop->popup( event->globalPos() ); event->accept(); } else { //otherwise, treat it as a regular click. emit clicked(); } } void FloatingIcon::paintEvent( QPaintEvent* ) { QPainter painter(this); painter.drawPixmap(0, 0, icon); } qjoypad-4.3.1/src/icon.h000066400000000000000000000010241350675246200150650ustar00rootroot00000000000000#ifndef QJOYPAD_ICON_H #define QJOYPAD_ICON_H #include #include #include #include #include #include "constant.h" class FloatingIcon : public QDialog { Q_OBJECT public: FloatingIcon( const QString &icon, QMenu *popup = 0, QWidget *parent = 0, const char *name = 0); signals: void clicked(); protected: void paintEvent(QPaintEvent* event); void mousePressEvent(QMouseEvent* event); private: QMenu* pop; QPixmap icon; }; #endif qjoypad-4.3.1/src/joypad.cpp000066400000000000000000000207151350675246200157660ustar00rootroot00000000000000#include #include "joypad.h" //for actually interacting with the joystick devices #include #include #include #include #include #include #include #include JoyPad::JoyPad( int i, int dev, QObject *parent ) : QObject(parent), joydev(-1), axisCount(0), buttonCount(0), jpw(0), readNotifier(0), errorNotifier(0) { 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. if (dev >= 0) { debug_mesg("Valid file handle, setting up handlers and reading axis configs...\n"); open(dev); debug_mesg("done resetting and setting up device index %d\n", i); } else { debug_mesg("This joypad does not have a valid file handle, not setting up event listeners\n"); } debug_mesg("Done constructing the joypad device %d\n", i); } JoyPad::~JoyPad() { close(); } void JoyPad::close() { if (readNotifier) { disconnect(readNotifier, 0, 0, 0); readNotifier->blockSignals(true); readNotifier->setEnabled(false); delete readNotifier; readNotifier = 0; } if (errorNotifier) { disconnect(errorNotifier, 0, 0, 0); errorNotifier->blockSignals(true); errorNotifier->setEnabled(false); delete errorNotifier; errorNotifier = 0; } if (joydev >= 0) { if (::close(joydev) != 0) { debug_mesg("close(js%d %d): %s\n", index, joydev, strerror(errno)); } joydev = -1; } } void JoyPad::open(int dev) { debug_mesg("resetting to dev\n"); //remember the device file descriptor close(); joydev = dev; char id[256]; memset(id, 0, sizeof(id)); if (ioctl(joydev, JSIOCGNAME(sizeof(id)), id) < 0) { deviceId = "Unknown"; } else { deviceId = id; } //read in the number of axes / buttons axisCount = 0; ioctl (joydev, JSIOCGAXES, &axisCount); buttonCount = 0; ioctl (joydev, JSIOCGBUTTONS, &buttonCount); //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 = axes.size(); i < axisCount; i++) { axes.append(new Axis( i, this )); } for (int i = buttons.size(); i < buttonCount; i++) { buttons.append(new Button( i, this )); } debug_mesg("Setting up joyDeviceListeners\n"); readNotifier = new QSocketNotifier(joydev, QSocketNotifier::Read, this); connect(readNotifier, SIGNAL(activated(int)), this, SLOT(handleJoyEvents())); errorNotifier = new QSocketNotifier(joydev, QSocketNotifier::Exception, this); connect(errorNotifier, SIGNAL(activated(int)), this, SLOT(handleJoyEvents())); debug_mesg("Done setting up joyDeviceListeners\n"); debug_mesg("done resetting to dev\n"); } const QString &JoyPad::getDeviceId() const { return deviceId; } QString JoyPad::getName() const { return tr("Joystick %1 (%2)").arg(index+1).arg(deviceId); } int JoyPad::getIndex() const { return index; } void JoyPad::toDefault() { //to reset the whole, reset all the parts. foreach (Axis *axis, axes) { axis->toDefault(); } foreach (Button *button, buttons) { button->toDefault(); } } bool JoyPad::isDefault() { //if any of the parts are not at default, then the whole isn't either. foreach (Axis *axis, axes) { if (!axis->isDefault()) return false; } foreach (Button *button, buttons) { if (!button->isDefault()) return false; } return true; } bool JoyPad::readConfig( QTextStream &stream ) { toDefault(); QString word; QChar ch = 0; int num = 0; stream >> word; while (!word.isNull() && word != "}") { word = word.toLower(); if (word == "button") { stream >> num; if (num > 0) { stream >> ch; if (ch != ':') { errorBox(tr("Layout file error"), tr("Expected ':', found '%1'.").arg(ch)); return false; } for (int i = buttons.size(); i < num; ++ i) { buttons.append(new Button(i, this)); } if (!buttons[num-1]->read( stream )) { errorBox(tr("Layout file error"), tr("Error reading Button %1").arg(num)); return false; } } else { stream.readLine(); } } else if (word == "axis") { stream >> num; if (num > 0) { stream >> ch; if (ch != ':') { errorBox(tr("Layout file error"), tr("Expected ':', found '%1'.").arg(ch)); return false; } for (int i = axes.size(); i < num; ++ i) { axes.append(new Axis(i, this)); } if (!axes[num-1]->read(stream)) { errorBox(tr("Layout file error"), tr("Error reading Axis %1").arg(num)); return false; } } } else { errorBox(tr("Layout file error"), tr("Error while reading layout. Unrecognized word: %1").arg(word)); return false; } stream >> word; } return true; } //only actually writes something if this JoyPad is NON DEFAULT. void JoyPad::write( QTextStream &stream ) { if (!axes.empty() || !buttons.empty()) { stream << "Joystick " << (index+1) << " {\n"; foreach (Axis *axis, axes) { if (!axis->isDefault()) { axis->write(stream); } } foreach (Button *button, buttons) { if (!button->isDefault()) { button->write(stream); } } stream << "}\n\n"; } } void JoyPad::release() { foreach (Axis *axis, axes) { axis->release(); } foreach (Button *button, buttons) { button->release(); } } void JoyPad::jsevent(const 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. unsigned int type = msg.type & ~JS_EVENT_INIT; if (type == JS_EVENT_AXIS) { debug_mesg("DEBUG: passing on an axis event\n"); debug_mesg("DEBUG: %d %d\n", msg.number, msg.value); if (msg.number < axes.size()) axes[msg.number]->jsevent(msg.value); else debug_mesg("DEBUG: axis index out of range: %d\n", msg.value); } else if (type == JS_EVENT_BUTTON) { debug_mesg("DEBUG: passing on a button event\n"); debug_mesg("DEBUG: %d %d\n", msg.number, msg.value); if (msg.number < buttons.size()) buttons[msg.number]->jsevent(msg.value); else debug_mesg("DEBUG: button index out of range: %d\n", 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() { js_event msg; ssize_t len = read(joydev, &msg, sizeof(js_event)); //if there was a real event waiting, if (len == 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 = 0; } void JoyPad::errorRead() { debug_mesg("There was an error reading off of the device with fd %d, disabling\n", joydev); close(); debug_mesg("Done disabling device with fd %d\n", joydev); } void JoyPad::focusChange(bool focusState) { hasFocus = !focusState; } qjoypad-4.3.1/src/joypad.h000066400000000000000000000050271350675246200154320ustar00rootroot00000000000000#ifndef QJOYPAD_JOYPAD_H #define QJOYPAD_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" #include #include #include class JoyPadWidget; //represents an actual joystick device class JoyPad : public QObject { Q_OBJECT friend class JoyPadWidget; friend class QuickSet; public: JoyPad( int i, int dev, QObject* parent ); ~JoyPad(); // close file descriptor and socket notifier void close(); //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( const js_event& msg ); //reset to default settings void toDefault(); //true iff this is currently at default settings bool isDefault(); //read the dimensions on the real joystick and use them void open( int dev ); const QString& getDeviceId() const; QString getName() const; int getIndex() const; private: //it's just easier to have these publicly available. int joydev; //the actual file descriptor to the joystick device char axisCount; //the number of axes available on this device char buttonCount; //the number of buttons public: //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(); 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. QList axes; QList 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 *readNotifier; QSocketNotifier *errorNotifier; QString deviceId; bool hasFocus; public slots: void handleJoyEvents(); void errorRead(); void focusChange(bool windowHasFocus); }; #endif qjoypad-4.3.1/src/joypadw.cpp000066400000000000000000000065041350675246200161550ustar00rootroot00000000000000#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 */ layoutMain = new QGridLayout(this); layoutMain->setSpacing(5); layoutMain->setMargin(5); flashcount = 0; int insertCounter = 0; quickset = NULL; foreach (Axis *axis, joypad->axes) { AxisWidget *aw = new AxisWidget(axis,this); axes.append(aw); connect( aw, SIGNAL( flashed( bool ) ), this, SLOT( flash( bool ))); layoutMain->addWidget(aw, insertCounter / 2, insertCounter % 2); insertCounter++; } foreach (Button *button, joypad->buttons) { ButtonWidget *bw = new ButtonWidget(button,this); buttons.append(bw); connect( bw, SIGNAL( flashed( bool ) ), this, SLOT( flash( bool ))); layoutMain->addWidget(bw, insertCounter / 2, insertCounter % 2); insertCounter++; } if (insertCounter % 2 == 1) { insertCounter ++; } insertCounter += 2; btnClear = new QPushButton(QIcon::fromTheme("edit-clear"), tr("Clear"), this); connect(btnClear, SIGNAL(clicked()), this, SLOT(clear())); layoutMain->addWidget(btnClear, insertCounter / 2, insertCounter % 2); insertCounter++; btnAll = new QPushButton(tr("Quick Set"), this); layoutMain->addWidget(btnAll, insertCounter / 2, insertCounter % 2); connect(btnAll, 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() { foreach (AxisWidget *axis, axes) { axis->update(); } foreach (ButtonWidget *button, buttons) { button->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, this); quickset->exec(); update(); delete quickset; quickset = NULL; } void JoyPadWidget::jsevent( const js_event& msg ) { //notify the component this event applies to. this cannot generate anything //other than a flash :) unsigned int type = msg.type & ~JS_EVENT_INIT; if (type == JS_EVENT_AXIS) { if (msg.number < axes.size()) axes[msg.number]->jsevent(msg.value); else debug_mesg("DEBUG: axis index out of range: %d\n", msg.value); } else if (type == JS_EVENT_BUTTON) { if (msg.number < buttons.size()) buttons[msg.number]->jsevent(msg.value); else debug_mesg("DEBUG: button index out of range: %d\n", msg.value); } //if we're doing quickset, it needs to know when we do something. if (quickset != NULL) { quickset->jsevent(msg); } } qjoypad-4.3.1/src/joypadw.h000066400000000000000000000037251350675246200156240ustar00rootroot00000000000000#ifndef QJOYPAD_JOYPAD_WIDGET_H #define QJOYPAD_JOYPAD_WIDGET_H //parts for the widget //Added by qt3to4: #include #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(const 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); private: //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 *layoutMain; QList axes; QList buttons; QPushButton *btnClear, *btnAll; //the quickset window, when we create it QuickSet* quickset; }; #endif qjoypad-4.3.1/src/joyslider.cpp000066400000000000000000000173371350675246200165120ustar00rootroot00000000000000#include "joyslider.h" //Added by qt3to4: JoySlider::JoySlider( int dz, int xz, int val, QWidget* parent ) :QWidget( parent ) { //initialize :) boxwidth = 0; boxheight = 0; rboxstart = 0; rboxend = 0; lboxstart = 0; lboxend = 0; twidth = 0; tend = 0; throttle = 0; dragState = DragNone; 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().window().color() ); paint.setBrush( (isEnabled())?Qt::white:palette().window() ); 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(); //see if this happened near one of the tabs. If so, start dragging that tab. if (throttle <= 0 && abs( xpt - pointFor( xzone, true )) < 5) dragState = DragXZ; else if (throttle <= 0 && abs( xpt - pointFor( deadzone, true )) < 5) dragState = DragDZ; else if (throttle >= 0 && abs( xpt - pointFor( xzone, false )) < 5) dragState = DragXZ; else if (throttle >= 0 && abs( xpt - pointFor( deadzone, false )) < 5) dragState = DragDZ; else dragState = DragNone; } void JoySlider::mouseReleaseEvent( QMouseEvent* ) { //when the mouse releases, all dragging stops. dragState = DragNone; } void JoySlider::mouseMoveEvent( QMouseEvent* e ) { //get the x coordinate int xpt = e->x(); //if we're dragging, move the appropriate tab! switch (dragState) { case DragXZ: xzone = valueFrom( xpt ); break; case DragDZ: deadzone = valueFrom( xpt ); break; default: return; } //if we moved a tab, redraw! update(); } qjoypad-4.3.1/src/joyslider.h000066400000000000000000000036361350675246200161540ustar00rootroot00000000000000#ifndef QJOYPAD_JOYSLIDER_H #define QJOYPAD_JOYSLIDER_H //the parent of this #include #include #include #include #include #include #include #include #include #include #include "constant.h" class JoySlider : public QWidget { Q_OBJECT enum DragState { DragNone = 0, //When dragState == DragXZ, that means we are currently dragging the xZone mark DragXZ = 1, //When dragState == DragDZ, that means we are currently dragging the deadZone mark DragDZ = 2 }; 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 deadZone() { 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 DragState dragState; //the axis' position int joyval; //the dead and extreme zone values int deadzone; int xzone; }; #endif qjoypad-4.3.1/src/keycode.cpp000066400000000000000000000071741350675246200161270ustar00rootroot00000000000000#include #include "keycode.h" #include "keydialog.hpp" #include const QString ktos( int keycode ) { if (keycode > MAXKEY || keycode < 0) keycode = 0; if (keycode == 0) return "[NO KEY]"; QString xname = XKeysymToString( XkbKeycodeToKeysym( QX11Info::display(), keycode, 0, 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?tr("Mouse %1").arg(val):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! int retValue = KeyDialog::getKey(buttonname, mouse, &mouseClicked, this->window()); // -1 is a special value meaning that the window was simply // closed so we can ignore this if (retValue < 0) { return; } else { value = retValue; } //if the return value was a mouse click... if (mouseClicked) { setText(tr("Mouse %1").arg(value)); } //otherwise, it was a key press! else { setText(ktos(value)); } } qjoypad-4.3.1/src/keycode.h000066400000000000000000000012741350675246200155670ustar00rootroot00000000000000#ifndef QJOYPAD_KEYCODE_H #define QJOYPAD_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 ); //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.3.1/src/keydialog.cpp000066400000000000000000000050611350675246200164450ustar00rootroot00000000000000#include "keydialog.hpp" #include #include KeyDialog::KeyDialog( const QString& button, bool m, QWidget* parent ) : QDialog( parent ), m_acceptMouse( m ), m_text( this ), m_isMouse( false ), m_value( 0 ) { setWindowTitle( "Choose a key" ); m_text.setText( QString( "Choose a new key %1for %2\n(Ctrl-X for no key)" ). arg( m ? "or mouse button " : "" ).arg( button ) ); m_text.adjustSize(); setFixedSize( m_text.width() + 20, m_text.height() + 20 ); m_text.move( 10, 10 ); } int KeyDialog::getKey( const QString& button, bool acceptMouse, bool* mouseOut, QWidget* parent ) { bool isMouse = false; quint32 value = 0; QPointer dialog = new KeyDialog( button, acceptMouse, parent ); if ( dialog->exec() != QDialog::Accepted ) { delete dialog; return -1; } if ( !dialog ) { return -1; } isMouse = dialog->m_isMouse; value = dialog->m_value; delete dialog; if ( mouseOut ) { *mouseOut = isMouse; } return value; } void KeyDialog::keyReleaseEvent( QKeyEvent* event ) { if ( event->key() != Qt::Key_Control ) { return; } m_value = event->nativeScanCode(); m_isMouse = false; accept(); } void KeyDialog::keyPressEvent( QKeyEvent* event ) { if ( event->key() == Qt::Key_Control ) { return; } if ( event->key() == Qt::Key_X && QGuiApplication::keyboardModifiers() == Qt::ControlModifier ) { m_value = 0; } else { m_value = event->nativeScanCode(); } m_isMouse = false; accept(); } static quint32 qtMouseButtonToXButton( Qt::MouseButton button ) { switch ( button ) { case Qt::LeftButton: return 1; case Qt::MiddleButton: return 2; case Qt::RightButton: return 3; default: return 0; } } void KeyDialog::mouseReleaseEvent( QMouseEvent* event ) { if ( !m_acceptMouse ) { return; } m_value = qtMouseButtonToXButton( event->button() ); m_isMouse = true; accept(); } void KeyDialog::wheelEvent( QWheelEvent *event ) { if ( !m_acceptMouse ) { return; } const QPoint delta = event->angleDelta(); const int x = delta.x(); const int y = delta.y(); if (y != 0) { if (y < 0) { m_value = 4; } else { m_value = 5; } } else if (x != 0) { if (x < 0) { m_value = 6; } else { m_value = 7; } } else { return; } m_isMouse = true; accept(); } qjoypad-4.3.1/src/keydialog.hpp000066400000000000000000000011731350675246200164520ustar00rootroot00000000000000#pragma once #include #include #include #include #include class KeyDialog : public QDialog { Q_OBJECT protected: bool m_acceptMouse; QLabel m_text; bool m_isMouse; quint32 m_value; KeyDialog( const QString& button, bool m = false, QWidget* parent = 0 ); void keyPressEvent( QKeyEvent* ); void keyReleaseEvent( QKeyEvent* ); void mouseReleaseEvent( QMouseEvent* ); void wheelEvent( QWheelEvent *event ); public: static int getKey( const QString& button, bool acceptMouse = false, bool* mousePressed = 0, QWidget* parent = 0 ); }; qjoypad-4.3.1/src/layout.cpp000066400000000000000000000540651350675246200160220ustar00rootroot00000000000000#include #include #include #include #include #include "layout.h" #include "config.h" //initialize things and set up an icon :) LayoutManager::LayoutManager( bool useTrayIcon, const QString &devdir, const QString &settingsDir ) : devdir(devdir), settingsDir(settingsDir), layoutGroup(new QActionGroup(this)), updateDevicesAction(new QAction(QIcon::fromTheme("view-refresh"),tr("Update &Joystick Devices"),this)), updateLayoutsAction(new QAction(QIcon::fromTheme("view-refresh"),tr("Update &Layout List"),this)), quitAction(new QAction(QIcon::fromTheme("application-exit"),tr("&Quit"),this)), le(0) { #ifdef WITH_LIBUDEV udevNotifier = 0; udev = 0; monitor = 0; if (!initUDev()) { errorBox(tr("UDev Error"), tr("Error creating UDev monitor. " "QJoyPad will still work, but it won't automatically update the joypad device list.")); } #endif //prepare the popup first. fillPopup(); //make a tray icon if (useTrayIcon) { QSystemTrayIcon *tray = new QSystemTrayIcon(this); tray->setContextMenu(&trayMenu); tray->setIcon(QIcon(QJOYPAD_ICON24)); tray->show(); connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayClick(QSystemTrayIcon::ActivationReason))); } //or make a floating icon else { FloatingIcon* icon = new FloatingIcon(QJOYPAD_ICON64,&trayMenu,0,"tray"); connect(icon, SIGNAL(clicked()), this, SLOT(iconClick())); connect(icon, SIGNAL(rejected()), qApp, SLOT(quit())); connect(icon, SIGNAL(accepted()), qApp, SLOT(quit())); icon->show(); } connect(updateLayoutsAction, SIGNAL(triggered()), this, SLOT(fillPopup())); connect(updateDevicesAction, SIGNAL(triggered()), this, SLOT(updateJoyDevs())); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); //no layout loaded at start. setLayoutName(QString()); } LayoutManager::~LayoutManager() { if (le) { le->close(); le = 0; } #ifdef WITH_LIBUDEV if (udevNotifier) { udevNotifier->blockSignals(true); } if (monitor) { udev_monitor_unref(monitor); monitor = 0; } if (udev) { udev_unref(udev); udev = 0; } #endif } #ifdef WITH_LIBUDEV bool LayoutManager::initUDev() { udev = udev_new(); debug_mesg("init udev\n"); if (udev) { debug_mesg("udev ok\n"); monitor = udev_monitor_new_from_netlink(udev, "udev"); if (monitor) { debug_mesg("monitor ok\n"); int errnum = udev_monitor_filter_add_match_subsystem_devtype( monitor, "input", NULL); if (errnum != 0) { debug_mesg("udev_monitor_filter_add_match_subsystem_devtype: %s\n", strerror(-errnum)); udev_monitor_unref(monitor); udev_unref(udev); monitor = 0; udev = 0; return false; } errnum = udev_monitor_enable_receiving(monitor); if (errnum != 0) { debug_mesg("udev_monitor_enable_receiving: %s\n", strerror(-errnum)); udev_monitor_unref(monitor); udev_unref(udev); monitor = 0; udev = 0; return false; } udevNotifier = new QSocketNotifier(udev_monitor_get_fd(monitor), QSocketNotifier::Read, this); connect(udevNotifier, SIGNAL(activated(int)), this, SLOT(udevUpdate())); debug_mesg("notifier ok\n"); } else { udev_unref(udev); udev = 0; } } return udev != 0; } void LayoutManager::udevUpdate() { struct udev_device *dev = udev_monitor_receive_device(monitor); if (dev) { QRegExp devicename("/js(\\d+)$"); QString path = udev_device_get_devnode(dev); const char *action = udev_device_get_action(dev); if (devicename.indexIn(path) >= 0) { int index = devicename.cap(1).toInt(); if (strcmp(action,"add") == 0 || strcmp(action,"online") == 0) { addJoyPad(index, path); } else if (strcmp(action,"remove") == 0 || strcmp(action,"offline") == 0) { removeJoyPad(index); } else if (strcmp(action,"change") == 0) { removeJoyPad(index); addJoyPad(index, path); } fillPopup(); if (le) { le->updateJoypadWidgets(); } } udev_device_unref(dev); } } #endif QString LayoutManager::getFileName(const QString& layoutname ) { return QString("%1%2.lyt").arg(settingsDir, layoutname); } bool LayoutManager::load(const QString& name) { //it's VERY easy to load NL :) if (name.isNull()) { clear(); return true; } QFile file(getFileName(name)); //if the file isn't available, if (!file.exists()) { errorBox(tr("Load error"), tr("Failed to find a layout named %1.").arg(name), le); return false; } //if the file isn't readable, if (!file.open(QIODevice::ReadOnly)) { errorBox(tr("Load error"), tr("Error reading from file: %1").arg(file.fileName()), le); 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" foreach (JoyPad *joypad, joypads) { joypad->toDefault(); } //start reading joypads! QTextStream stream( &file ); bool okay = false; int num = 0; QChar ch = 0; QString word; while (!stream.atEnd()) { stream >> word; if (word.isNull()) break; //if this line is specifying a joystick if (word.compare(QLatin1String("joystick"), Qt::CaseInsensitive) == 0) { stream >> word; num = word.toInt(&okay); //make sure the number of the joystick is valid if (!okay || num < 1) { errorBox(tr("Load error"), tr("Error reading joystick definition. Unexpected token \"%1\". Expected a positive number.").arg(word), le); if (name != currentLayout) reload(); else clear(); return false; } stream.skipWhiteSpace(); stream >> ch; if (ch != QChar('{')) { errorBox(tr("Load error"), tr("Error reading joystick definition. Unexpected character \"%1\". Expected '{'.").arg(ch), le); if (name != currentLayout) reload(); else clear(); return false; } int index = num - 1; //if there was no joypad defined for this index before, make it now! if (joypads[index] == 0) { joypads.insert(index, new JoyPad(index, -1, this)); } //try to read the joypad, report error on fail. if (!joypads[index]->readConfig(stream)) { errorBox(tr("Load error"), tr("Error reading definition for joystick %1.").arg(index), le); //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; } } else if (word.startsWith('#')) { // ignore comment stream.readLine(); } else { errorBox(tr("Load error"), tr("Error reading joystick definition. Unexpected token \"%1\". Expected \"Joystick\".").arg(word), le); if (name != currentLayout) reload(); else clear(); return false; } } //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.isEmpty()) { 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... foreach (JoyPad *joypad, joypads) { joypad->toDefault(); } //and call our layout NL setLayoutName(QString()); } void LayoutManager::save() { if (currentLayout.isNull()) { saveAs(); } else { save(getFileName(currentLayout)); } } void LayoutManager::save(const QString &filename) { QFile file(filename); save(file); } void LayoutManager::save(QFile &file) { //if it's good, start writing the file if (file.open(QIODevice::WriteOnly)) { QTextStream stream( &file ); stream << "# " QJOYPAD_NAME " Layout File\n\n"; foreach (JoyPad *joypad, joypads) { joypad->write( stream ); } file.close(); } //if it's not, error. else { errorBox(tr("Save error"), tr("Could not open file %1, layout not saved.").arg(file.fileName()), le); } } void LayoutManager::saveAs() { bool ok = false; //request a new name! QString name = QInputDialog::getText(le, tr("Name new layout - %1").arg(QJOYPAD_NAME), tr("Enter a name for the new layout:"), QLineEdit::Normal, QString(), &ok); if (!ok) { return; } else if (name.isEmpty()) { errorBox(tr("Save error"), tr("Layout name cannot be empty."), le); return; } else if (name.contains('/')) { errorBox(tr("Save error"), tr("Layout name may not contain a '/' (slash)."), le); return; } QFile file(getFileName(name)); //don't overwrite an existing layout. if (file.exists()) { errorBox(tr("Save error"), tr("That name's already taken!"), le); 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(file); //add the new name to our lists fillPopup(); if (le) { le->updateLayoutList(); } } void LayoutManager::importLayout() { QFileDialog dialog(le); dialog.setWindowTitle(tr("Import layout - %1").arg(QJOYPAD_NAME)); dialog.setFileMode(QFileDialog::ExistingFile); dialog.setAcceptMode(QFileDialog::AcceptOpen); QStringList filters; filters.append(tr("QJoyPad layout files (*.lyt)")); filters.append(tr("Any files (*)")); dialog.setNameFilters(filters); dialog.setDefaultSuffix("lyt"); if (dialog.exec() && !dialog.selectedFiles().isEmpty()) { QString sourceFile = dialog.selectedFiles()[0]; QFileInfo info(sourceFile); QString layoutName = info.baseName(); if (layoutName.endsWith(".lyt",Qt::CaseInsensitive)) { layoutName.truncate(layoutName.size() - 4); } QString filename = getFileName(layoutName); if (info == QFileInfo(filename)) { errorBox(tr("Import error"), tr("Cannot import file from QJoyPad settings directory.")); return; } if (QFile::exists(filename)) { if (QMessageBox::warning(le, QString("%1 - %2").arg(tr("Layout exists"), QJOYPAD_NAME), tr("Layout %1 exists. Do you want to overwrite it?"), tr("Over&write"), tr("&Cancel"), QString(), 0, 1) == 1) { return; } QFile::remove(filename); } QFile::copy(sourceFile, filename); fillPopup(); if (le) { le->updateLayoutList(); } load(layoutName); } } void LayoutManager::exportLayout() { QFileDialog dialog(le); dialog.setWindowTitle(tr("Export layout - %1").arg(QJOYPAD_NAME)); dialog.setFileMode(QFileDialog::AnyFile); dialog.setAcceptMode(QFileDialog::AcceptSave); QStringList filters; filters.append(tr("QJoyPad layout files (*.lyt)")); filters.append(tr("Any files (*)")); dialog.setNameFilters(filters); dialog.setDefaultSuffix("lyt"); if (dialog.exec() && !dialog.selectedFiles().isEmpty()) { save(dialog.selectedFiles()[0]); } } void LayoutManager::saveDefault() { QFile file( settingsDir + "layout"); if (file.open(QIODevice::WriteOnly)) { QTextStream(&file) << currentLayout; file.close(); } } void LayoutManager::remove() { if (currentLayout.isNull()) return; if (QMessageBox::warning(le, tr("Delete layout? - %1").arg(QJOYPAD_NAME), tr("Remove layout %1 permanently from your hard drive?").arg(currentLayout), tr("&Delete"), tr("&Cancel"), QString(), 0, 1 ) == 1) { return; } QString filename = getFileName( currentLayout ); if (!QFile(filename).remove()) { errorBox(tr("Remove error"), tr("Could not remove file %1").arg(filename), le); } fillPopup(); if (le) { le->updateLayoutList(); } clear(); } void LayoutManager::rename() { if (currentLayout.isNull()) return; bool ok = false; QString name = QInputDialog::getText(le, tr("Rename layout - %1").arg(QJOYPAD_NAME), tr("Enter a new name for the layout:"), QLineEdit::Normal, currentLayout, &ok); if (!ok) { return; } else if (name.isEmpty()) { errorBox(tr("Rename error"), tr("Layout name cannot be empty."), le); return; } else if (name.contains('/')) { errorBox(tr("Rename error"), tr("Layout name may not contain a '/' (slash)."), le); return; } QString filename = getFileName(name); if (QFile::exists(filename)) { errorBox(tr("Rename error"), tr("Layout with name %1 already exists.").arg(name), le); return; } if (!QFile::rename(getFileName(currentLayout), filename)) { errorBox(tr("Rename error"), tr("Error renaming layout."), le); return; } fillPopup(); if (le) { le->updateLayoutList(); } load(name); } QStringList LayoutManager::getLayoutNames() const { //goes through the list of .lyt files and removes the file extensions ;) QStringList result = QDir(settingsDir).entryList(QStringList("*.lyt")); for (int i = 0; i < result.size(); ++ i) { QString& name = result[i]; name.truncate(name.length() - 4); } return result; } void LayoutManager::setLayoutName(const QString& name) { QList actions = layoutGroup->actions(); for (int i = 0; i < actions.size(); ++ i) { QAction* action = actions[i]; if (action->data().toString() == name) { action->setChecked(true); break; } } currentLayout = name; if (le) { le->setLayout(name); } } void LayoutManager::iconClick() { //don't show the dialog if there aren't any joystick devices plugged in if (available.isEmpty()) { errorBox(tr("No joystick devices available"), tr("No joystick devices are currently available to configure.\nPlease plug in a gaming device and select\n\"Update Joystick Devices\" from the popup menu."), le); return; } if (le) { if (le->isActiveWindow()) { le->close(); } else { le->activateWindow(); } 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::layoutTriggered() { QAction *action = qobject_cast(sender()); //if they clicked on a Layout name, load it! if (action) { load(action->data().toString()); } } void LayoutManager::fillPopup() { //start with an empty slate trayMenu.clear(); //add in the Update options trayMenu.addAction(updateLayoutsAction); trayMenu.addAction(updateDevicesAction); trayMenu.addSeparator(); //add null layout QAction *action = trayMenu.addAction(tr("[NO LAYOUT]")); action->setCheckable(true); action->setActionGroup(layoutGroup); //put a check by the current one ;) if (currentLayout.isNull()) { action->setChecked(true); } connect(action, SIGNAL(triggered()), this, SLOT(layoutTriggered())); //then add all the layout names foreach (const QString &name, getLayoutNames()) { QString title = name; title.replace('&',"&&"); action = trayMenu.addAction(title); action->setData(name); action->setCheckable(true); action->setActionGroup(layoutGroup); //put a check by the current one ;) if (currentLayout == name) { action->setChecked(true); } connect(action, SIGNAL(triggered()), this, SLOT(layoutTriggered())); } trayMenu.addSeparator(); //and, at the end, quit! trayMenu.addAction(quitAction); } void LayoutManager::updateJoyDevs() { debug_mesg("updating joydevs\n"); //reset all joydevs to sentinal value (-1) foreach (JoyPad *joypad, joypads) { joypad->close(); } //clear out the list of previously available joysticks available.clear(); QRegExp devicename("/js(\\d+)$"); #ifdef WITH_LIBUDEV // try to enumerate devices using udev, if compiled with udev support bool udev_ok = false; if (udev) { struct udev_enumerate *enumerate = udev_enumerate_new(udev); if (enumerate) { int errnum = udev_enumerate_add_match_subsystem(enumerate, "input"); if (errnum == 0) { errnum = udev_enumerate_scan_devices(enumerate); if (errnum == 0) { struct udev_list_entry *devices, *dev_list_entry; devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { const char *path = udev_list_entry_get_name(dev_list_entry); struct udev_device *dev = udev_device_new_from_syspath(udev, path); if (dev) { QString devpath = udev_device_get_devnode(dev); if (devicename.indexIn(devpath) >= 0) { int index = devicename.cap(1).toInt(); addJoyPad(index, devpath); } udev_device_unref(dev); } } udev_ok = true; } else { debug_mesg("udev_enumerate_scan_devices: %s\n", strerror(-errnum)); } } else { debug_mesg("udev_enumerate_add_match_subsystem: %s\n", strerror(-errnum)); } udev_enumerate_unref(enumerate); } } // but if udev failed still try "ls $devdir/js*" if (!udev_ok) { debug_mesg("udev enumeration failed. retry with \"ls $devdir/js*\"\n"); #endif //set all joydevs anew (create new JoyPad's if necesary) QDir deviceDir(devdir); QStringList devices = deviceDir.entryList(QStringList("js*"), QDir::System); //for every joystick device in the directory listing... //(note, with devfs, only available devices are listed) foreach (const QString &device, devices) { if (devicename.indexIn(device) >= 0) { int index = devicename.cap(1).toInt(); QString devpath = QString("%1/%2").arg(devdir, device); addJoyPad(index, devpath); } } #ifdef WITH_LIBUDEV } #endif //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::addJoyPad(int index) { addJoyPad(index, QString("%1/js%2").arg(devdir, index)); } void LayoutManager::addJoyPad(int index, const QString& devpath) { debug_mesg("opening %s\n", qPrintable(devpath)); //try opening the device. int joydev = open(qPrintable(devpath), O_RDONLY | O_NONBLOCK); //if it worked, then we have a live joystick! Make sure it's properly //setup. if (joydev >= 0) { JoyPad* joypad = joypads[index]; //if we've never seen this device before, make a new one! if (joypad == 0) { joypad = new JoyPad( index, joydev, this ); joypads.insert(index,joypad); } else { debug_mesg("found previously open joypad with index %d, ignoring", index); joypad->open(joydev); } //make this joystick device available. available.insert(index,joypad); } else { perror(qPrintable(devpath)); } } void LayoutManager::removeJoyPad(int index) { JoyPad *joypad = available[index]; if (joypad) { joypad->close(); available.remove(index); } } qjoypad-4.3.1/src/layout.h000066400000000000000000000066551350675246200154710ustar00rootroot00000000000000#ifndef QJOYPAD_LAYOUT_H #define QJOYPAD_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 #include "config.h" #ifdef WITH_LIBUDEV #include #endif //a layout handles several joypads #include "joypad.h" //for errors #include "error.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" //handles loading, saving, and changing of layouts class LayoutManager : public QObject { friend class LayoutEdit; Q_OBJECT public: LayoutManager(bool useTrayIcon, const QString &devdir, const QString &settingsDir); ~LayoutManager(); //produces a list of the names of all the available layout. QStringList getLayoutNames() const; 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(); void save(const QString& filename); void save(QFile& file); //save the current layout with a new name void saveAs(); void exportLayout(); void importLayout(); //save the currently loaded layout so it can be recalled later void saveDefault(); //get rid of a layout void remove(); //rename current layout void rename(); //when the tray icon is clicked void iconClick(); void trayClick(QSystemTrayIcon::ActivationReason reason); //rebuild the popup menu with the current information void fillPopup(); //update the list of available joystick devices void updateJoyDevs(); private slots: //when the user selects an item on the tray's popup menu void layoutTriggered(); private: void addJoyPad(int index); void addJoyPad(int index, const QString& devpath); void removeJoyPad(int index); //change to the given layout name and make all the necesary adjustments void setLayoutName(const QString& name); //get the file name for a layout name QString getFileName(const QString& layoutname); //the directory in wich the joystick devices are (e.g. "/dev/input") QString devdir; QString settingsDir; //the layout that is currently in use QString currentLayout; //the popup menu from the tray/floating icon QMenu trayMenu; //known actions for the popup menu QActionGroup *layoutGroup; QAction *updateDevicesAction; QAction *updateLayoutsAction; QAction *quitAction; //if there is a LayoutEdit open, this points to it. Otherwise, NULL. QPointer le; QHash available; QHash joypads; #ifdef WITH_LIBUDEV bool initUDev(); QSocketNotifier *udevNotifier; struct udev *udev; struct udev_monitor *monitor; private slots: void udevUpdate(); #endif }; #endif qjoypad-4.3.1/src/layout_edit.cpp000066400000000000000000000170111350675246200170150ustar00rootroot00000000000000#include "layout_edit.h" #include "config.h" //build the dialog LayoutEdit::LayoutEdit( LayoutManager* l ) : QWidget(0), lm(l), mainLayout(0), padStack(0), joyButtons(0), cmbLayouts(0), btnAdd(0), btnRem(0), btnUpd(0), btnRev(0), btnExport(0), btnImport(0), btnRename(0) { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle( QJOYPAD_NAME ); setWindowIcon(QPixmap(QJOYPAD_ICON64)); mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(5); mainLayout->setMargin(5); QFrame* frame = new QFrame(this); frame->setFrameStyle(QFrame::Box | QFrame::Sunken); QGridLayout* g = new QGridLayout(frame); g->setMargin(5); g->setSpacing(5); cmbLayouts = new QComboBox(frame); connect(cmbLayouts, SIGNAL(activated(int)), this, SLOT(load(int))); //most of these buttons can link directly into slots in the LayoutManager btnAdd = new QPushButton(frame); btnAdd->setIcon(QIcon::fromTheme("list-add")); btnAdd->setToolTip(tr("Add Layout")); if (btnAdd->icon().isNull()) { btnAdd->setText("+"); } btnAdd->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(btnAdd, SIGNAL(clicked()), lm, SLOT(saveAs())); btnRem = new QPushButton(frame); btnRem->setIcon(QIcon::fromTheme("list-remove")); if (btnRem->icon().isNull()) { btnRem->setText("-"); } btnRem->setToolTip(tr("Remove Layout")); btnRem->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(btnRem, SIGNAL(clicked()), lm, SLOT(remove())); btnRename = new QPushButton(tr("&Rename"), frame); btnRename->setToolTip(tr("Rename Layout")); btnRename->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(btnRename, SIGNAL(clicked()), lm, SLOT(rename())); QHBoxLayout *layoutLayout = new QHBoxLayout(); layoutLayout->addWidget(cmbLayouts); layoutLayout->addWidget(btnAdd); layoutLayout->addWidget(btnRem); layoutLayout->addWidget(btnRename); mainLayout->addLayout(layoutLayout); btnImport = new QPushButton(QIcon::fromTheme("document-open"), tr("&Import"), frame); connect(btnImport, SIGNAL(clicked()), lm, SLOT(importLayout())); g->addWidget(btnImport,1,0); btnExport = new QPushButton(QIcon::fromTheme("document-save-as"), tr("E&xport"), frame); connect(btnExport, SIGNAL(clicked()), lm, SLOT(exportLayout())); g->addWidget(btnExport,1,1); btnUpd = new QPushButton(QIcon::fromTheme("document-save"), tr("&Save"), frame); connect(btnUpd, SIGNAL(clicked()), lm, SLOT(save())); g->addWidget(btnUpd,1,2); btnRev = new QPushButton(QIcon::fromTheme("document-revert"), tr("Re&vert"), frame); connect(btnRev, SIGNAL(clicked()), lm, SLOT(reload())); g->addWidget(btnRev,1,3); mainLayout->addWidget( frame ); //produce a list of names for the FlashRadioArray //this is only necesary since joystick devices need not always be //contiguous QStringList names; foreach (JoyPad *joypad, lm->available) { names.append(joypad->getName()); connect(this, SIGNAL(focusStateChanged(bool)), joypad, SLOT(focusChange(bool))); } //flash radio array joyButtons = new FlashRadioArray(names, true, this ); mainLayout->addWidget( joyButtons ); //we have a WidgetStack to represent the multiple joypads padScroll = new QScrollArea(this); padScroll->setWidgetResizable(true); mainLayout->addWidget(padScroll); padStack = new QStackedWidget( this ); padStack->setFrameStyle(QFrame::Box | QFrame::Sunken ); padScroll->setWidget(padStack); //go through each of the available joysticks // i is the current index into PadStack int i = 0; foreach (JoyPad *joypad, lm->available) { //add a new JoyPadWidget to the stack padStack->insertWidget( i, joypad->widget(padStack,i) ); //every time it "flashes", flash the associated tab. connect( padStack->widget(i), SIGNAL( flashed( int ) ), joyButtons, SLOT( flash( int ))); ++i; } //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. QPushButton* close = new QPushButton(QIcon::fromTheme("window-close"), tr("&Close Dialog"), this ); connect(close, SIGNAL(clicked()), this, SLOT(close())); QPushButton* quit = new QPushButton(QIcon::fromTheme("application-exit"), tr("&Quit"), this ); connect( quit, SIGNAL( clicked() ), qApp, SLOT(quit())); QHBoxLayout* h = new QHBoxLayout(); h->setMargin(0); h->setSpacing(5); h->addWidget(close); h->addWidget(quit); mainLayout->addLayout(h); connect(qApp, SIGNAL(focusChanged ( QWidget * , QWidget * ) ), this, SLOT(appFocusChanged(QWidget *, QWidget *))); this->show(); } void LayoutEdit::setLayout(const QString &layout) { //change the selection for (int i = 0; i < cmbLayouts->count(); ++ i) { if (cmbLayouts->itemData(i).toString() == layout) { cmbLayouts->setCurrentIndex(i); break; } } bool hasLayout = !layout.isNull(); btnRem->setEnabled(hasLayout); btnRename->setEnabled(hasLayout); //update all the JoyPadWidgets. for (int i = 0, n = lm->available.count(); i < n; i++) { ((JoyPadWidget*)padStack->widget(i))->update(); } } void LayoutEdit::updateLayoutList() { //blank the list, then load in new names from the LayoutManager. cmbLayouts->clear(); cmbLayouts->addItem(tr("[NO LAYOUT]"), QVariant(QString())); if (lm->currentLayout.isNull()) { cmbLayouts->setCurrentIndex(0); } foreach (const QString& layout, lm->getLayoutNames()) { cmbLayouts->addItem(layout, layout); if (layout == lm->currentLayout) { cmbLayouts->setCurrentIndex(cmbLayouts->count() - 1); } } } void LayoutEdit::updateJoypadWidgets() { int indexOfFlashRadio = mainLayout->indexOf(joyButtons); FlashRadioArray *newJoyButtons; QStringList names; foreach (JoyPad *joypad, lm->available) { names.append(joypad->getName()); } newJoyButtons = new FlashRadioArray( names, true, this ); mainLayout->insertWidget(indexOfFlashRadio, newJoyButtons); mainLayout->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)); } int i = 0; foreach (JoyPad *joypad, lm->available) { //add a new JoyPadWidget to the stack padStack->insertWidget( i, joypad->widget(padStack,i) ); //every time it "flashes", flash the associated tab. connect( padStack->widget(i), SIGNAL( flashed( int ) ), joyButtons, SLOT( flash( int ))); ++i; } } void LayoutEdit::appFocusChanged(QWidget *old, QWidget *now) { if (now != NULL && old == NULL) { emit focusStateChanged(false); } else if(old != NULL && now == NULL) { emit focusStateChanged(true); foreach (JoyPad *joypad, lm->available) { debug_mesg("iterating and releasing\n"); joypad->release(); } debug_mesg("done releasing!\n"); } } void LayoutEdit::load(int index) { lm->load(cmbLayouts->itemData(index).toString()); } qjoypad-4.3.1/src/layout_edit.h000066400000000000000000000022151350675246200164620ustar00rootroot00000000000000#ifndef QJOYPAD_LAYOUT_EDIT_H #define QJOYPAD_LAYOUT_EDIT_H #include #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 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(const QString& layout); //update the list of available layouts void updateLayoutList(); void updateJoypadWidgets(); signals: void focusStateChanged(bool); public slots: void appFocusChanged(QWidget *old, QWidget *now); private slots: void load(int index); protected: //the layout manager this represents LayoutManager* lm; //parts of the dialog: QVBoxLayout *mainLayout; QScrollArea *padScroll; QStackedWidget *padStack; FlashRadioArray *joyButtons; QComboBox* cmbLayouts; QPushButton *btnAdd, *btnRem, *btnUpd, *btnRev, *btnExport, *btnImport, *btnRename; }; #endif qjoypad-4.3.1/src/main.cpp000066400000000000000000000215311350675246200154210ustar00rootroot00000000000000//for ouput when there is no GUI going #include #include //to create and handle signals for various events #include #include //to create a qapplication #include #include #include #include #include //to load layouts #include "layout.h" //to give event.h the current X11 display #include "event.h" //to produce errors! #include "error.h" #include "config.h" //variables needed in various functions in this file QPointer layoutManagerPtr; //signal handler for SIGUSR2 //SIGUSR2 means that a new layout should be loaded. It is saved in // ~/.qjoypad/layout, where the last used layout is put. void catchSIGUSR2( int sig ) { if (layoutManagerPtr) layoutManagerPtr->load(); //remember to catch this signal again next time. signal( sig, catchSIGUSR2 ); } //signal handler for SIGUSR1 //SIGUSR1 means that we should update the available joystick device list. void catchSIGUSR1( int sig ) { //buildJoyDevices(); if (layoutManagerPtr) layoutManagerPtr->updateJoyDevs(); //remember to catch this signal again next time. signal( sig, catchSIGUSR1 ); } int main( int argc, char **argv ) { //create a new event loop. This will be captured by the QApplication //when it gets created QApplication app( argc, argv ); QTranslator translator; app.setQuitOnLastWindowClosed(false); if (translator.load(QLocale::system(), "qjoypad", "_", QJOYPAD_L10N_DIR)) { app.installTranslator(&translator); } else { debug_mesg("no translation for locale: %s\n", qPrintable(QLocale::system().name())); } //where QJoyPad saves its settings! const QString settingsDir(QDir::homePath() + "/.qjoypad3/"); //where to look for settings. If it does not exist, it will be created QDir dir(settingsDir); //the directory in wich the joystick devices are (e.g. "/dev/input") QString devdir = QJOYPAD_DEVDIR; //if there is no new directory and we can't make it, complain if (!dir.exists() && !dir.mkdir(settingsDir)) { errorBox(app.translate("main","Couldn't create the QJoyPad save directory"), app.translate("main","Couldn't create the QJoyPad save directory: %s").arg(settingsDir)); 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 struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"device", required_argument, 0, 'd'}, {"force-tray", no_argument, 0, 't'}, {"notray", no_argument, 0, 'T'}, {"update", no_argument, 0, 'u'}, {0, 0, 0, 0 } }; for (;;) { int c = getopt_long(argc, argv, "hd:tTu", long_options, NULL); if (c == -1) break; switch (c) { case 'h': printf("%s", qPrintable(app.translate("main","%1\n" "Usage: %2 [--device=\"/device/path\"] [--notray|--force-tray] [\"layout name\"]\n" "\n" "Options:\n" " -h, --help Print this help message.\n" " -d, --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" " -t, --force-tray Force to use a system tray icon.\n" " -T, --notray Do not use a system tray icon. This is useful for\n" " window managers that don't support this feature.\n" " -u, --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").arg(QJOYPAD_NAME, argc > 0 ? argv[0] : "qjoypad"))); return 0; case 'd': if (QFileInfo(optarg).isDir()) { devdir = optarg; } else { errorBox(app.translate("main","Not a directory"), app.translate("main","Path is not a directory: %1").arg(optarg)); return 1; } break; case 'T': useTrayIcon = false; break; case 't': useTrayIcon = true; forceTrayIcon = true; break; case 'u': update = true; break; case '?': fprintf(stderr, "%s", qPrintable(app.translate("main", "Illeagal argument.\n" "See `%1 --help` for more information\n").arg(argc > 0 ? argv[0] : "qjoypad"))); return 1; } } if (optind < argc) { layout = argv[optind ++]; if (optind < argc) { fprintf(stderr, "%s", qPrintable(app.translate("main", "Too many arguments.\n" "See `%1 --help` for more information\n").arg(argc > 0 ? argv[0] : "qjoypad"))); return 1; } } //if the user specified a layout to use, if (!layout.isEmpty()) { //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( &file ) << 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 = 0; 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.isEmpty() && !update) errorBox(app.translate("main","Instance Error"), app.translate("main","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) { kill(pid,SIGUSR1); } if (!layout.isEmpty()) { kill(pid,SIGUSR2); } } //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(); } if (forceTrayIcon) { int sleepCounter = 0; while (!QSystemTrayIcon::isSystemTrayAvailable()) { sleep(1); sleepCounter++; if (sleepCounter > 20) { errorBox(app.translate("main","System tray isn't loading"), app.translate("main","Waited more than 20 seconds for the system tray to load. Giving up.")); return 1; } } } //create a new LayoutManager with a tray icon / floating icon, depending //on the user's request LayoutManager layoutManager(useTrayIcon,devdir,settingsDir); layoutManagerPtr = &layoutManager; //build the joystick device list for the first time, //buildJoyDevices(); layoutManager.updateJoyDevs(); //load the last used layout (Or the one given as a command-line argument) layoutManager.load(); //prepare the signal handlers signal( SIGUSR1, catchSIGUSR1 ); signal( SIGUSR2, catchSIGUSR2 ); //and run the program! int result = app.exec(); //when everything is done, save the current layout for next time... layoutManager.saveDefault(); //remove the lock file... pidFile.remove(); //and terminate! return result; } qjoypad-4.3.1/src/quickset.cpp000066400000000000000000000041651350675246200163310ustar00rootroot00000000000000#include "quickset.h" #include "keydialog.hpp" //build the dialog QuickSet::QuickSet( JoyPad* jp, QWidget *parent) : QDialog(parent), joypad(jp), setting(false) { setWindowTitle(tr("Set %1").arg(jp->getName())); QVBoxLayout* LMain = new QVBoxLayout(this); LMain->setMargin(5); LMain->setSpacing(5); //LMain->setAutoAdd(true); QLabel *temp = new QLabel(tr("Press any button or axis and\nyou will be prompted for a key."),this); LMain->addWidget(temp); QPushButton* button = new QPushButton(tr("Done"),this); LMain->addWidget(button); connect( button, SIGNAL(clicked()), this, SLOT(accept())); } void QuickSet::jsevent(const 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 unsigned int type = msg.type & ~JS_EVENT_INIT; if (type == JS_EVENT_BUTTON) { //capture that button. if (msg.number < joypad->buttons.size()) { Button* button = joypad->buttons[msg.number]; //go into setting mode and request a key/mousebutton setting = true; bool isMouse = false; int code = KeyDialog::getKey(button->getName(), true, &isMouse, this); setting = false; if (code >= 0) { button->setKey(isMouse, code); } } } else if (type == JS_EVENT_AXIS) { //require a signal strength of at least 5000 to consider an axis moved. if (abs(msg.value) < 5000) return; //capture the axis that moved if (msg.number < joypad->axes.size()) { Axis* axis = joypad->axes[msg.number]; //grab a keycode for that axis and that direction setting = true; bool isMouse = false; int code = KeyDialog::getKey((msg.value >= 0 ? tr("%1, positive") : tr("%1, negative")).arg(axis->getName()), true, &isMouse, this); setting = false; //assign the key to the axis. if (code >= 0) { axis->setKey(isMouse, (msg.value > 0), code); } } } } qjoypad-4.3.1/src/quickset.h000066400000000000000000000014751350675246200157770ustar00rootroot00000000000000#ifndef QJOYPAD_QUICKSET_H #define QJOYPAD_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 { Q_OBJECT public: QuickSet(JoyPad* jp, QWidget *parent = 0); //this needs to see js_events so it can capture them directly void jsevent( const 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.3.1/translations/000077500000000000000000000000001350675246200157215ustar00rootroot00000000000000qjoypad-4.3.1/translations/qjoypad_de.ts000066400000000000000000000677451350675246200204330ustar00rootroot00000000000000 Axis Axis %1 Achse %1 KEYBOARD/MOUSE TASTATUR/MAUS MOUSE MAUS KEYBOARD TASTATUR THROTTLE Drossel AxisEdit &Gradient &Verlauf Keyboard/Mouse Button Tastatur-/Maus-Taste Mouse (Vert.) Maus (Vert.) Mouse (Vert. Rev.) Maus (Vert. Inv.) Mouse (Hor.) Maus (Hor.) Mouse (Hor. Rev.) Maus (Hor. Inv.) Linear Linear Quadratic Quadratisch Cubic Cubisch Quadratic Extreme Quadratisches Extrem Power Function Potenzfunktion &Mouse Speed &Mausgeschwindigkeit &Sensitivity &Sensibilität Neg. Throttle Neg. Drossel No Throttle Keine Drossel Pos. Throttle Pos. Drossel Button Button %1 Taste %1 %1 : Mouse %2 %1: Maus %2 %1 : %2 %1: %2 ButtonEdit Set %1 Setze %1 &Sticky &Klebrig &Rapid Fire &Schnellfeuer FloatingIcon %1 Floating Icon %1 Schwebendes Symbol GetKey Choose a key Wähle eine Taste Choose a new key or mouse button for %1 Whähle eine neue Taste oder einen neuen Mausknopf für %1 Choose a new key for %1 Wähle eine neue Taste für %1 (Ctrl-X for no key) (Ctrl-X für keine Taste) JoyPad Joystick %1 (%2) Joystick %1 (%2) Layout file error Belegungsdatei-Fehler Expected ':', found '%1'. ';' erwartet, '%1' vorgefunden. Error reading Button %1 Fehler beim Lesen der Taste %1 Error reading Axis %1 Fehler beim Lesen der Achse %1 Error while reading layout. Unrecognized word: %1 Fehler beim Lesen der Belegung. Unbekanntes Wort: %1 JoyPadWidget Clear Löschen Quick Set Schnell Setzen KeyButton Mouse %1 Maus %1 LayoutEdit Add Layout Belegung Hinzufügen Remove Layout Belegung Entfernen &Rename &Umbenennen Rename Layout Belegung Umbenennen &Import &Importieren E&xport E&xportieren &Save &Speichern Re&vert &Zurücksetzen &Close Dialog &Fenster Schließen &Quit &Beenden [NO LAYOUT] [KEINE BELEGUNG] LayoutManager Update &Joystick Devices &Joysticks Aktualisieren Update &Layout List Be&legungsliste Aktualisieren &Quit &Beenden UDev Error UDev Fehler Error creating UDev monitor. QJoyPad will still work, but it won't automatically update the joypad device list. Fehler beim Erzeugen des UDev Monitors. QJoypad wird dennoch funktionieren, aber es wird nicht automatisch die Joypad-Liste aktualisieren. Load error Lade-Fehler Failed to find a layout named %1. Belegung mit Namen %1 wurde nicht gefunden. Error reading from file: %1 Fehler beim lesen der Datei: %1 Error reading joystick definition. Unexpected token "%1". Expected a positive number. Fehler beim Lesen der Joystick-Definition. Unerwartets Token "%1". Eine positive Zahl wurde erwartet. Error reading joystick definition. Unexpected character "%1". Expected '{'. Fehler beim Lesen der Joystick-Definition. Unerwartets Zeichen "%1". '{' wurde erwartet. Error reading definition for joystick %1. Fehler beim Lesen der Definition von Joystick %1. Error reading joystick definition. Unexpected token "%1". Expected "Joystick". Fehler beim Lesen der Joystick-Definition. Unerwartetes Token "%1". "Joystick" wurde erwertet. Save error Fehler beim Speichern Could not open file %1, layout not saved. Konnte Datei %1 nicht öffnen, Belegung wurde nicht gespeichert. Name new layout - %1 Neue Belegung benennen - %1 Import layout - %1 Belegung Importieren - %1 QJoyPad layout files (*.lyt) QJoyPad Belegung-Dateien (*.lyt) Any files (*) Alle Dateien (*) Import error Fehler beim Importieren Cannot import file from QJoyPad settings directory. Kann Datei aus dem QJoyPad Einstellungsverzeichnis nicht importieren. Layout exists Belegung existiert Layout %1 exists. Do you want to overwrite it? Belegung %1 existiert. Wollen Sie sie überschreiben? Over&write Über&schreiben &Cancel &Abbrechen Export layout - %1 Belegung Exportieren - %1 Delete layout? - %1 Belegung löschen? - %1 &Delete &Löschen Enter a name for the new layout: Name der neuen Belegung eingeben: Layout name cannot be empty. Belegungsname kann nicht leer sein. Layout name may not contain a '/' (slash). Belegungsname kann keinen '/' (Schrägstrich) enthalten. That name's already taken! Dieser Name ist bereits belegt! Remove layout %1 permanently from your hard drive? Belegung %1 permanent von ihrer Festplatte löschen? Remove error Fehler beim Entfernen Could not remove file %1 Konnte Datei %1 nicht entfernen Rename layout - %1 Belegung umbenennen - %1 Enter a new name for the layout: Geben Sie einen neuen Belegungsnamen ein: Rename error Umbenennungsfehler Layout with name %1 already exists. Belegung mit dem Namen %1 existiert bereits. Error renaming layout. Fehler beim Umbenennen einer Belegung. No joystick devices available Keine Joysticks verfügbar No joystick devices are currently available to configure. Please plug in a gaming device and select "Update Joystick Devices" from the popup menu. Zur Zeit sind keine Joysticks zur Konfiguration verfügbar. Bitte schließen Sie ein entsprechendes Gerät an und wählen Sie "Joysticks Aktualisieren" im Popup-Menü. [NO LAYOUT] [KEINE BELEGUNG] QuickSet Set %1 Setze %1 Press any button or axis and you will be prompted for a key. Drücken sie eine belibige Taste oder Achse und Sie werden nach einer Taste gefragt werden. Done Fertig %1, positive %1, positiv %1, negative %1, negativ main Couldn't create the QJoyPad save directory Konnte das QJoyPad Seicher-Verzeichnis nicht anlegen Couldn't create the QJoyPad save directory: %s Konnte das QJoyPad Seicher-Verzeichnis nicht anlegen: %s %1 Usage: %2 [--device="/device/path"] [--notray|--force-tray] ["layout name"] Options: -h, --help Print this help message. -d, --device=PATH Look for joystick devices in PATH. This should be something like "/dev/input" if your game devices are in /dev/input/js0, /dev/input/js1, etc. -t, --force-tray Force to use a system tray icon. -T, --notray Do not use a system tray icon. This is useful for window managers that don't support this feature. -u, --update Force a running instance of QJoyPad to update its list of devices and layouts. "layout name" Load the given layout in an already running instance of QJoyPad, or start QJoyPad using the given layout. %1 Verwendung: %2 [--device="/Geräte/Pfad"] [--notray|--force-tray] ["Belegungsname"] Optionen: -h, --help Gibt diesen Hilfetext aus. -d, --device=PFAD In PFAD nach Joystick-Geräten suchen. Dies sollte "/dev/input" sein wenn Ihre Joysticks /dev/input/js0, /dev/input/js1 etc. sind. -t, --force-tray Verwendung der Systemablage erzwingen. -T, --notray Systemablage nicht verwenden. Nützlich für Fenstermanager die keine Systemablage unterstützen. -u, --update Zwinge eine laufende Instanz von QJoyPad die Geräte- und Belegungslisten zu aktualisieren. "Belegungsname" Lade die angegebenen Belegung in einer bereits laufenden Instanz von QJoyPad oder starte QJoyPad mit der gegebenen Belegung. Not a directory Kein Verzeichnis Path is not a directory: %1 Pfad ist kein Verzeichnis: %1 Illeagal argument. See `%1 --help` for more information Ungülltiges Argument. Siehe `%1 --help` für mehr Information Too many arguments. See `%1 --help` for more information Zu viele Argumente. Siehe `%1 --help` für mehr Information Instance Error Instanzierungs-Fehler There is already a running instance of QJoyPad; please close the old instance before starting a new one. Es läuft bereits eine Instanz von QJoyPad; bitte schließen sie die alte Instanz bevor sie eine neue öffnen. System tray isn't loading Systemablage startet nicht Waited more than 20 seconds for the system tray to load. Giving up. Es wurde mehr als 20 Sekunden auf die Systemablage gewartet. Gebe auf.