pax_global_header00006660000000000000000000000064117475337240014527gustar00rootroot0000000000000052 comment=15795ed0897c159f5743b6d668b83e295e16efaa olpc-kbdshim-27/000077500000000000000000000000001174753372400136755ustar00rootroot00000000000000olpc-kbdshim-27/20-olpc-kbdshim.fdi000066400000000000000000000024731174753372400171620ustar00rootroot00000000000000 olpc-kbdshim-hal -l -f -R /var/run/olpc-kbdshim_command -A /var/run/powerevents -r /usr/bin/olpc-rotate -V /usr/bin/olpc-volume -b /usr/bin/olpc-brightness olpc-kbdshim-hal -l -f -R /var/run/olpc-kbdshim_command -A /var/run/powerevents -r /usr/bin/olpc-rotate -V /usr/bin/olpc-volume -b /usr/bin/olpc-brightness olpc-kbdshim-27/COPYING000066400000000000000000000432771174753372400147450ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 Appendix: 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) 19yy 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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) 19yy 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. olpc-kbdshim-27/Makefile000066400000000000000000000075341174753372400153460ustar00rootroot00000000000000# Copyright (C) 2009 Paul G. Fox # Licensed under the terms of the GNU GPL v2 or later; see COPYING for details. PACKAGE=olpc-kbdshim VERSION=27 FDIST=fc15 # PGF_CROSS=1 ifneq ($(PGF_CROSS),) CC=arm-linux-gnueabi-gcc UDEVLIB=arm-cross/libudev.so.0 CFLAGS = -DPGF_CROSS else UDEVLIB = -ludev endif # don't edit .spec -- edit .spec.tmpl SPEC=$(PACKAGE).spec DATETAG=$(shell date +%Y%m%d) GITHEAD=git$(shell test -d .git && git rev-parse --short HEAD ) ifeq ($(do_rel),) SNAP=.$(DATETAG)$(GITHEAD) endif RELEASE=$(shell cat .spec_release 2>/dev/null || echo error) SRELEASE=$(RELEASE)$(SNAP) TARBALL=$(PKGVER)-$(GITHEAD).tar.gz SRPM=$(PKGVER)-$(SRELEASE).src.rpm MOCK=./mock-wrapper -r fedora-14-i386 --resultdir=$(MOCKDIR) MOCKDIR=./rpms CWD=$(shell pwd) PKGVER=$(PACKAGE)-$(VERSION) # may be set externally to RPM_OPT_FLAGS OPT_FLAGS ?= -O2 -g ##### # PROGS = olpc-kbdshim olpc-kbdshim-hal olpc-kbdshim-udev PROGS = olpc-kbdshim olpc-kbdshim-udev all: $(PROGS) # all: olpc-kbdshim-hal # the "hal" version runs as a HAL addon daemon, and needs several # libraries for the HAL interfaces olpc-kbdshim-hal: \ CFLAGS += -Wall $(OPT_FLAGS) -DVERSION=$(VERSION) \ $$(pkg-config --cflags hal) \ $$(pkg-config --cflags glib-2.0) \ $$(pkg-config --cflags dbus-glib-1) olpc-kbdshim-hal: \ LDLIBS += $$(pkg-config --libs hal) \ $$(pkg-config --libs glib-2.0) \ $$(pkg-config --libs dbus-glib-1) olpc-kbdshim-hal: olpc-kbdshim-hal.o common.o $(CC) -o $@ olpc-kbdshim-hal.o common.o $(LDLIBS) # the udev version relies only on libudev olpc-kbdshim-udev: \ CFLAGS += -Wall $(OPT_FLAGS) -DVERSION=$(VERSION) LDLIBS = $(UDEVLIB) olpc-kbdshim-udev: olpc-kbdshim-udev.o common.o $(CC) -o $@ olpc-kbdshim-udev.o common.o $(LDLIBS) # non-hal version of kbdshim needs nothing. the downside is that # it only monitors the local keyboard and touchpad, and won't detect # user activity on USB devices. olpc-kbdshim: CFLAGS += -Wall $(OPT_FLAGS) -DVERSION=$(VERSION) olpc-kbdshim: olpc-kbdshim.o common.o common.h $(CC) -o $@ olpc-kbdshim.o common.o $(LDLIBS) olpc-kbdshim.o olpc-kbdshim-hal.o common.o: common.h ##### # testing targets tarball: $(TARBALL) srpm: $(SRPM) public_html: $(TARBALL) $(SRPM) rpms/$(PKGVER)-$(SRELEASE).$(FDIST).i686.rpm scp $(TARBALL) $(SRPM) \ crank:public_html/rpms/srpms scp rpms/$(PKGVER)-$(SRELEASE).$(FDIST).i686.rpm \ crank:public_html/rpms public_rpms: $(TARBALL) $(SRPM) rpms/$(PKGVER)-$(SRELEASE).$(FDIST).i686.rpm scp $(TARBALL) $(SRPM) rpms/$(PKGVER)-$(SRELEASE).$(FDIST).i686.rpm \ crank:public_rpms/f14 privdist: scp rpms/$(PKGVER)-$(SRELEASE).$(FDIST).i686.rpm \ crank:public_html/private_rpms ssh crank ln -sf \ $(PKGVER)-$(SRELEASE).$(FDIST).i686.rpm \ public_html/private_rpms/$(PKGVER)-$(RELEASE).latest.rpm # create the real spec (carefully!) so it refers to a) our tarball, and # b) our prerelease string. $(SPEC): ALWAYS sed \ -e 's/__VERSION__/$(VERSION)/' \ -e 's/__RELEASE__/$(SRELEASE)/' \ -e 's/__TARBALL__/$(TARBALL)/' \ $(SPEC).tmpl > $(SPEC) # build the tarball directly from git. # THIS MEANS NO UNCOMMITED CHANGES WILL BE INCLUDED!!! $(TARBALL): -git diff --exit-code # working copy is clean? -git diff --cached --exit-code # uncommitted changes? git archive --format=tar --prefix=$(PKGVER)/ HEAD | gzip -c > $@ # build the SRPM from the spec and the tarball $(SRPM): $(SPEC) $(TARBALL) rpmbuild --define "_specdir $(CWD)" \ --define "_sourcedir $(CWD)" \ --define "_builddir $(CWD)" \ --define "_srcrpmdir $(CWD)" \ --define "_rpmdir $(CWD)" \ --define "dist %nil" \ --nodeps -bs $(SPEC) # build rpm from the srpm mock: $(SRPM) @mkdir -p $(MOCKDIR) $(MOCK) -q --init $(MOCK) --installdeps $(SRPM) $(MOCK) -v --no-clean --rebuild $(SRPM) clean: rm -f *.o $(PROGS) -$(RM) $(SRPM) $(TARBALL) -$(RM) -rf $(MOCKDIR) .PHONY: tarball srpm mock ALWAYS olpc-kbdshim-27/README000066400000000000000000000171661174753372400145700ustar00rootroot00000000000000 olpc-kbdshim ---------- Note -- there are several versions of olpc-kbdshim -- one integrated with udev, another with HAL, and the other a "standalone" daemon. The first two can monitor USB input devices, the third cannot. The HAL version is no longer supported or maintained, and the "standalone" version will receive only minimal support. ---------- This daemon, handles several distinct tasks for the XO. The tasks are related by a need to have full access to keyboard and touchpad activity, so all keyboard and mouse keys are intercepted. "Grab" key support: While a grab key is depressed, it transforms touchpad motion events into scroll events (i.e., into presses of virtual buttons 4, 5, 6, and 7). The rate at which this tranformation occurs it tunable, as well as the "sign" of the tranformation. The grab keys also affect the operation of the keyboard up/down/ left/right arrows -- they, too, will cause scroll events when one of the grab keys is held. On non-XO (i.e., USB) keyboards, the 'grab' keys are the two "logo" keys, and are treated the same as the XO grab keys. Touchpad and D-pad rotation and reflection: When the screen is rotated, it makes sense to rotate the actions of the D-pad arrows and the touchpad to match. When in e-book mode, it might be desirable to reflect the touchpad to make it useful with the laptop halves partially opened. (In this case the d-pad is not reflected.) These translations are activated with small commands injected into a fifo the daemon creates for the purpose. (I.e., there is no direct knowledge of screen orientation, but an external script provides it.) User (in)activity: Since this daemon is watching the user's input devices, it can readily generate events related to the user being idle. Event are generated after each of three successive idle timeouts, as well as for the user becoming active again. These events take the form of writes to a filesystem path (which is probably a fifo created by the olpc-powerd package). Key binding: Five XO-specific keys (rotate, and the up/down keys for brightness and speaker volume) can be bound to commands to be run when they are pressed. The commands that control olpc-kbdshim are read from a command fifo (named by the -R option). All commands are single characters -- some have arguments. Touchpad and D-pad rotation commands: n - normal i - invert r - rotate right (see below for notes about rotation) l - rotate left (see below for notes about rotation) Correct rotation can be maintained simply by giving the same keyword to this daemon as is given to xrandr: # xrandr -o $new && echo $new >/var/run/olpc-kbdshim_commands Reflection: X - reflect the X axis Y - reflect the Y axis Z - reflect both axes x - stop reflecting the X axis y - stop reflecting the Y axis z - stop reflecting both axes Reflection cannot be set automatically, because it doesn't have sufficient sensors. However, if one is working in ebook mode and wishes to have the touchpad oriented to match the screen, then one can use: # echo Z >/var/run/olpc-kbdshim_commands To restore normal orientation, use: # echo z >/var/run/olpc-kbdshim_commands User activity: I [ N1 [ N2 [ N3 ] ] ] When no keyboard or touchpad activity has been seen for N1, N2, and N3 seconds, successively, the strings "useridle1", "useridle2", and "useridle3" will be written to the pathname specified with the -A option. Once the user has become idle (i.e., N1 has been passed), then their keystroke or mouse movement will trigger a "useractive" event. The 'I' command can be used at any time to reset the timers, in which case the activity monitor is fully reset along with the timers (so the next activity will generate "useractive"). Specifying N1 as 0 will suppress all activity events. Omitting N2 or N3 will cause them to be set to N1+1 or N2+1. Example: # echo I 120 240 600 >/var/run/olpc-kbdshim_commands Local/non-local input devices: All keyboards and pointer devices connected to the laptop will be monitored for user activity. All keyboards and pointer devices will participate in the translations caused by the "grab" (or "logo") keys. Only the XO's local touchpad, and only the XO's local D-pad (which is essentially the XO's numeric keypad arrows), will have their behavior modified by rotation. Only the XO's local brightness, volume, and "rotate" keys will be bound to their respective commands. (The local brightness and volume keys can be modified by the local alt keys to request "min" and "max" values, rather than an incremental change.) Further configuration: The keyboard and input devices which are considered "local" can be changed using the -K and -T options. (Useful if the only keyboard or touchpad is a USB model, for instance.) Since the keyboard and touchpad are inaccessible when in ebook mode, kbdshim will suppress those events when in that mode. Use "-e 0" to disable this feature. (Events from the game keys will still be reported, of course.) The identity of the keys used for "grabbing" can be changed with the -g or -G options. The amount of pointer movement need to cause a scroll event during grab operation can be set with -q. To allow better control of scrolling in just one direction, the -n option can be used: if either the vertical or horizontal motion is less than N percent (normally 33%) of the other, then the smaller of the two will be dropped entirely. The relationship between pointer movement and scrolling direction can be inverted with the -v option. The daemon can be put into a realtime scheduling class with -s. Logging can be forced to syslog with -l. The -d (enable more logging) and -X (don't transmit) can aid debugging. Rotation: Trac tickets (at dev.laptop.org) #9350 and #10380 deal with issues regarding the rotation of the screen and touchpad. The goal, of course, is to make the screen, touchpad, and d-pad all rotate in the direction printed on the button when the button is pushed. On the widely-distributed XO-1 build 802 and earlier, the screen goes the wrong way when the button is pushed, because of a buglet in the X11 driver. This was fixed in later drivers, so /usr/bin/olpc-rotate assumes that xrandr will rotate the screen in the correct direction when given "left" and "right" directives. ("left" means counterclockwise, "right" means clockwise.) Later, it was discovered that SWRandR on the XO-1.5 may also rotate the screen incorrectly. To allow for further "errant" cases, olpc-rotate now honors the presence of a flag file (/var/run/olpc-rotate-reverse). When this flag is present, xrandr will be instructed to go left instead of right, and vice versa. Creating this flag is left to other software on the system. In addition, a "-r" flag to olpc-rotate tells it to reset rotation of the touchpad (and d-pad), presumably because the screen is known to be in the upright orientation. This can be used when bringing up or restarting X. (Note that we've never expected the game keys (circle, square, check, cross) to rotate -- they have labels on them, so rotating doesn't make sense.) olpc-kbdshim-27/arm-cross/000077500000000000000000000000001174753372400156035ustar00rootroot00000000000000olpc-kbdshim-27/arm-cross/libudev.h000066400000000000000000000203051174753372400174060ustar00rootroot00000000000000/* * libudev - interface to udev device information * * Copyright (C) 2008-2010 Kay Sievers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. */ #ifndef _LIBUDEV_H_ #define _LIBUDEV_H_ #include #include #include #ifdef __cplusplus extern "C" { #endif /* * udev - library context * * reads the udev config and system environment * allows custom logging */ struct udev; struct udev *udev_ref(struct udev *udev); void udev_unref(struct udev *udev); struct udev *udev_new(void); void udev_set_log_fn(struct udev *udev, void (*log_fn)(struct udev *udev, int priority, const char *file, int line, const char *fn, const char *format, va_list args)); int udev_get_log_priority(struct udev *udev); void udev_set_log_priority(struct udev *udev, int priority); const char *udev_get_sys_path(struct udev *udev); const char *udev_get_dev_path(struct udev *udev); void *udev_get_userdata(struct udev *udev); void udev_set_userdata(struct udev *udev, void *userdata); /* * udev_list * * access to libudev generated lists */ struct udev_list_entry; struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name); const char *udev_list_entry_get_name(struct udev_list_entry *list_entry); const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); /** * udev_list_entry_foreach: * @list_entry: entry to store the current position * @first_entry: first entry to start with * * Helper to iterate over all entries of a list. */ #define udev_list_entry_foreach(list_entry, first_entry) \ for (list_entry = first_entry; \ list_entry != NULL; \ list_entry = udev_list_entry_get_next(list_entry)) /* * udev_device * * access to sysfs/kernel devices */ struct udev_device; struct udev_device *udev_device_ref(struct udev_device *udev_device); void udev_device_unref(struct udev_device *udev_device); struct udev *udev_device_get_udev(struct udev_device *udev_device); struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath); struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname); struct udev_device *udev_device_new_from_environment(struct udev *udev); /* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */ struct udev_device *udev_device_get_parent(struct udev_device *udev_device); struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype); /* retrieve device properties */ const char *udev_device_get_devpath(struct udev_device *udev_device); const char *udev_device_get_subsystem(struct udev_device *udev_device); const char *udev_device_get_devtype(struct udev_device *udev_device); const char *udev_device_get_syspath(struct udev_device *udev_device); const char *udev_device_get_sysname(struct udev_device *udev_device); const char *udev_device_get_sysnum(struct udev_device *udev_device); const char *udev_device_get_devnode(struct udev_device *udev_device); struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device); struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device); struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device); const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key); const char *udev_device_get_driver(struct udev_device *udev_device); dev_t udev_device_get_devnum(struct udev_device *udev_device); const char *udev_device_get_action(struct udev_device *udev_device); unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device); const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr); /* * udev_monitor * * access to kernel uevents and udev events */ struct udev_monitor; struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); void udev_monitor_unref(struct udev_monitor *udev_monitor); struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); /* kernel and udev generated events over netlink */ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name); /* custom socket (use netlink and filters instead) */ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path); /* bind socket */ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size); int udev_monitor_get_fd(struct udev_monitor *udev_monitor); struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); /* in-kernel socket filters to select messages that get delivered to a listener */ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype); int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag); int udev_monitor_filter_update(struct udev_monitor *udev_monitor); int udev_monitor_filter_remove(struct udev_monitor *udev_monitor); /* * udev_enumerate * * search sysfs for specific devices and provide a sorted list */ struct udev_enumerate; struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); void udev_enumerate_unref(struct udev_enumerate *udev_enumerate); struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); struct udev_enumerate *udev_enumerate_new(struct udev *udev); /* device properties filter */ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag); int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath); /* run enumeration with active filters */ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); /* return device list */ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); /* * udev_queue * * access to the currently running udev events */ struct udev_queue; struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue); void udev_queue_unref(struct udev_queue *udev_queue); struct udev *udev_queue_get_udev(struct udev_queue *udev_queue); struct udev_queue *udev_queue_new(struct udev *udev); unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue); unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue); int udev_queue_get_udev_is_active(struct udev_queue *udev_queue); int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue); int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum); int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, unsigned long long int start, unsigned long long int end); struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue); struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue); #ifdef __cplusplus } /* extern "C" */ #endif #endif olpc-kbdshim-27/arm-cross/libudev.so.0000077500000000000000000001263501174753372400177500ustar00rootroot00000000000000ELF(44 (pXXX$$x%%$$QtdGNUԇqڥM\F*YT \/CA !bie+ R^@ Pj" %jq 5C ABCFHJMNOPQRTWXYZ\]_`acefhijlnopqsuxyz|(LmҚ} {{iFy A~\gyF߄pél5:D>(bv4Uq-5֕luiP{0QF0{'  ޴HƊ WY+evғFV!X(=+^kP'xC5Q9&HAr՜ۇ |$%|L&RgZ,8%kYL.sU.@_->l0!q*qw D6ƊNI6g\'.3(ְ Z#h&~Op$IGnKq!ù $I 1W  wbInD @8p"m GA'2i63XQ5l !@k PP`YC )=~^ ]"Hr` u@ O g9  hp` ! @(q` :    $ kf p (F t! "  ^< |T +D ey cq` X^8 q` ^ & ȍ$ n  z,Z Y# r  P m  Xf !  )    ,  k? *$wt " L }$ 0d4 +; v$X   :  (!L Dc p` :  ELfp |!t ;  ЋP  D p` l, ; ~< `T 2  J8 Gx( B; +   Ȍ  g  :  E, t 4 rT !  SV| , |9 <U :  TSl  __gmon_start____cxa_finalize_Jv_RegisterClasses__fprintf_chk__vfprintf_chkstderr__aeabi_unwind_cpp_pr0udev_get_userdataudev_set_userdataudev_refudev_unreffreeudev_get_log_priorityudev_set_log_fnudev_get_sys_pathudev_get_dev_pathudev_list_entry_get_by_nameudev_set_log_priority__snprintf_chk__stack_chk_fail__stack_chk_guardudev_newcallocmallocmemcpygetenv__strdupfopen64fgets__ctype_b_locstrchrstrlenfclosestrcmp__aeabi_unwind_cpp_pr1udev_list_entry_get_nextudev_list_entry_get_nameudev_list_entry_get_valuestrtolstrncmpmemcmpmempcpyreadlinkstrrchrstrnlen__sprintf_chkudev_device_get_udevudev_device_refudev_device_unrefudev_device_get_devpathudev_device_get_syspathudev_device_get_sysnameudev_device_get_sysnumudev_device_get_driverudev_device_get_actionudev_device_get_seqnumudev_device_get_sysattr_value__lxstat64udev_device_get_tags_list_entryudev_device_get_subsystemudev_device_get_devlinks_list_entryudev_device_get_properties_list_entryudev_device_get_property_valuestrstrudev_device_new_from_syspath__xstat64udev_device_get_parentudev_device_new_from_subsystem_sysnameudev_device_new_from_devnumstrtoulludev_device_get_devnumudev_device_get_devtypeudev_device_get_parent_with_subsystem_devtypeudev_device_new_from_environmentudev_device_get_devnodefnmatchreallocudev_enumerate_newudev_enumerate_refudev_enumerate_unrefudev_enumerate_get_udevopendirreaddir64closedirudev_enumerate_get_list_entryqsortudev_enumerate_add_match_subsystemudev_enumerate_add_nomatch_subsystemudev_enumerate_add_match_sysattrudev_enumerate_add_nomatch_sysattrudev_enumerate_add_match_propertyudev_enumerate_add_match_tagudev_enumerate_add_match_sysnameudev_enumerate_add_syspathudev_enumerate_scan_devicesdirfdreadlinkatudev_enumerate_scan_subsystemsudev_monitor_new_from_socketudev_monitor_new_from_netlinkudev_monitor_filter_updatememsetsetsockoptudev_monitor_enable_receivingbindgetsocknameudev_monitor_set_receive_buffer_sizeudev_monitor_refudev_monitor_unrefudev_monitor_get_udevudev_monitor_get_fdudev_monitor_receive_devicerecvmsgpoll__errno_locationsendmsgudev_monitor_filter_add_match_subsystem_devtypeudev_monitor_filter_add_match_tagudev_monitor_filter_removeudev_queue_newudev_queue_refudev_queue_unrefudev_queue_get_udevudev_queue_get_kernel_seqnumfread__gcc_personality_v0_Unwind_Resumeudev_queue_get_udev_seqnumudev_queue_get_udev_is_activeudev_queue_get_queue_is_emptyudev_queue_get_seqnum_sequence_is_finishedudev_queue_get_seqnum_is_finishedudev_queue_get_queued_list_entryudev_queue_get_failed_list_entrylibgcc_s.so.1libc.so.6__environld-linux.so.3libudev.so.0GLIBC_2.4GCC_3.0GCC_3.5GCC_3.3.1u  ii  S @P&y  U&y  a_&  a ii  $$%% %T' X' \'`'d'3h'@&R&& &$&(&P,&0&4& 8&q<& @& D& H&L&P&T&X&S\&`&d&h&l&p&t&x&a|&&&N&&&&&&&&f&c&&& &O&M&&!&"&H&#&C&$&%&&&'&(&&)&d&*&X'+','- '.'0't'2'4 '5$'6('7,'80'94':8';<'<@'>D'nH'?L'@P'@-- ƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌxƏʌpƏʌhƏʌ`ƏʌXƏʌPƏʌHƏʌ@Əʌ8Əʌ0Əʌ(Əʌ ƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌxƏʌpƏʌhƏʌ`ƏʌXƏʌPƏʌHƏʌ@Əʌ8Əʌ0Əʌ(Əʌ ƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌƏʌ0 0 R/ <L8@-8@8P@0S8(00S 008d`@-,0,0 R 0S3/P<<0 @-@0 0  0H\q-M ($06 S  P0S  25 dH :P 0S  p 00S  :P   >  0MXuuuuuuuvuu vv$vA-M``@P p00Q P00PQPЍ`P@888 4  0,Ts0v tr@-@P 0S p@-` PS@P '@P / 0Pp`@Pp;@P @P  wPpp@-@UPT  ep3P0L p@-R@`: SP![30HmP 0pP0HPPPe0P LPH`1a0PT LP T p8@-P@T 1 !P8U#3888@-X@PP 00@ (08@8P000/p@-@Pp00CS0p (08@L0S PH0eL0PSH_p@\P/O-ğ !MM`< =0C080s@@KP 0 lV  0dZ  0[@PP `PJ  K80 000(Pp 0 $p(0 ,00.S2 :`P 5pP)  uP3 $0@E P P' `P"`P  P ]`P (<( 00RwDЍۍpP(P0 (P 4@ ` p@P4@P0    PP0w03 u 3  P4@ P0`P`P p P P4@ P,P ,`Po`P wp bP}MjwXX`uO-pp AMM@ `~P0PE0 PP P 0.S TPPt 0 $0R Ѝۍ 0u,X`uO-sPP,Mp T@@@PP ,ЍO+@  52H0L L0 0SR 2`002@002@0 0x20$0Hp冁T  0R] @Q3 P  @ `\ R  0L0 \*A pH` 0 L0pS@T0 0L T00@S!   P  0@@@0 00 @ BP   0P @R /R 0/R @ R$ P000i00 @,Ѝ  ?Pv\vhv0@-PP M@ Q Ѝ0 0{P 0@-PP M@ Q Ѝ0  0cP p@-`PM@P QЍp 0KP p@-`PM@P QЍp 03P p@-`PM@P QЍp|0 0P 0@-PP M@ Q Ѝ0e8 0P 0@-PP M@ Q Ѝ0M( 0P 8@-@P8P8PP 88"0O-2M M0 <[pG 8P@P[ P= =002删0JJ 0@ m 0`P @P 0.S က0 tP0 SUPPP@P 4P , 00R@Ѝۍ0P(@P  *YL"]P0PE@ 0S [PP xx  0  ` 0  @0,  0HXtvvvvvsA-Aa@0GMMP0t4wU% xp0pG0  P ppPP  0U0t$0RxЍۍ<pnP 05PXvsvu8@-@jPP 0@0yw8@-TQpPPtM`@tЍQ@ @P 030@S  P0: S! k 3r cpXPB@l 2020Yl 2zP444   0ЮvdwHw@-Q`PP Mp@ Q Pp@P  p 0020p,0 Ѝ|Pp @P LLL   0@\wwvdwHwO-M$MP @ 00C* 03ʍ PP000 03 0 0!0P@`D PP PPPpP`) PEPPP` @P $T BP"*+尣4$CPE`P0$,<0D0y0@pP !T:B`0C@  P PPT @( p00uʍ 1:C0C ` Z P1`@:C0C TpN5PP& = ajF`F `pHʍ !1:C*B0C B`P1:C0Cp`p a :F0C@   qGP LP:ʍ A 0}$Ѝڍ p @P00/3p@-aS M00P`# 0 @T  0@O ЍpPڌ @  t00S@   @P 0  0W@P00vwHw-PM  ! 0Ѝ@-@00P000/@-@P00CS0P@SP/P/hh'O-0MMP0ʍ0 0@T 070(0$7<P4X 0dtPl p , 40$J  0R0000@ 0$@ 0lP 0 6͍8 @ 00 `PP6 FR0S ,0S0S9  S60 SP@0ʍ0 0RmЍڍ0S  R 0@T( Pg0|S% 0V:)@P [  J V PWP`P `P,`P(  5PP [P )P`P `P P`P4$0 0P=z[00 V0S4P@dPg#3 ` #00 t33XSN,0SJ KPM 004#, 0 #`0 3@92P4"2 0` "0 %>@%0S Pl"2 ` `"00 `2@ P"82 0` "00 2@P"@!1 ` !00 1x!1 0` h!0 1@P(!`1 ` !00 <1@ @P 0  0 0`0 0 0x @PT h0 0` D 0 =c(XxxvxHww_wwKhxm0v4xgwYxvO-@c@s!M` M0H@0DPD8jPڰ3S  RE@0D(0RLЍۍP D<2P 0! DWP <0P kPP80 0C@0A 00@040@,8 @PŸ"|  1l!D  @!00L 0( 000P 2`8 #4<`4#\0P `8 #4<`4#`0(0,005P @P @ PP0 %d8$#4 ,<$d4#d h0 0  80 0CY0000T$0@ 04 0A 0X8@PWH\H P0|  @ uGDXxvxHwx$yP- M0 ЍQ  P P- M ЍQ  0P 0@-P M,@(0@ 0 Ѝ0h8@-@PP8PP 0@0&$8P000/@-@P00CS0TQ@P/E-@P@0CMM0,4@`0`F0pp H'`P   >Z 0,ĝ\ 4ЍۍC. 0%pC  0: X\yP  0/@- 0LPA-@P@0AM0p 4`0`F0 `P P0 $0RAލPi`@@@   0pXtyrLyyyH-@PM@0`K 0 P  0RK![ 01@ p0Р1[SРРĖXO-A Q M@0p*K 0( P` 0( 0R% K2[JZ 1! ) 0Z2[ S)( 0j(00 M  0X Р n РX0@-M?@P 0 P -P xP  !P Ѝ0- M 0P  Ѝ@- MpP0 P  @PP P ` P EP PE  PPUPЍ 0S:0S:PPM-M@pB  `P< 0S 8, [:6 " R 0S: r8( pj PP@ 0% $00Sp P [ Z, U(0TW0w3ЍR(0Z#pGJ- M 5P ЍO-QP 0FMM`P0\4 0\$0R`dЍۍ 00 $0\$]pP H!@0CN@ C @0C( ,0$BP&F ؠ(0 > 8 70 ,(8 P   05$P P  P jP -БXtO-Aq@02M M`P0<0,0RaЍۍ00"]P\1PE0 hP PP? =00C 0 1到000K0K PP, 0.S 倐0 @0 TP@@ڀ0@ 0 @  P 0 @Xydu@-udev_set_log_fnudev_newlibudev: %s: libudev/libudev.ccustom logging function %p registered %uUDEV_LOG/dev/sys/etc/udev/udev.confSYSFS_PATHUDEV_CONFIG_FILEremissing = in '%s'[%i], skip line inconsistent quoting in '%s'[%i], skip line udev_logudev_rootudev_rulesUDEV_ROOTcontext creation failed #+-.:=@_errinfodebug\x2f\x5c../driversubsystem\x%02xudev_device_read_dbudev_device_new_from_syspathudev_device_new_from_environmentmodule/.udev/db/:libudev/libudev-device.cerror reading db link %s: %m device %p filled with db symlink data '%s' device %p filled with db file data DEVLINKSTAGSMAJORMINOR%lluSEQNUMDEVNAMEDEVPATH_OLDDRIVERACTIONDEVTYPESUBSYSTEM/module//drivers/drivers/subsystem//class//bus/DEVPATHnot in sys :%s /devices//ueventdevice %p has devpath '%s' blockchar%s/dev/%s/%u:%uDEVTYPE=MAJOR=MINOR=DEVNAME=DEVPATH=SUBSYSTEM=DEVLINKS=TAGS=DRIVER=ACTION=DEVPATH_OLD=SEQNUM=TIMEOUT=missing values, invalid device /block/md/sound/card/controlC/.udev/tags//subsystemdevicesbusclass/block/dm-udev_monitor_new_from_socketudev_monitor_new_from_netlinkudev_monitor_enable_receivingudev_monitor_receive_deviceudev_monitor_send_devicelibudev/libudev-monitor.cerror getting socket: %m udevkernelbind failed: %m unable to receive message invalid message length unicast netlink message ignored multicast kernel netlink message from pid %d ignored no sender credentials received, message ignored sender uid=%d, message ignored libudevignored a message from an invalid release of udevadm (%x != %x) @/unrecognized message header @passed %zi bytes to socket monitor %p passed %zi bytes to netlink monitor %p open_queue_file/kernel/uevent_seqnum/.udev/queue.binlibudev/libudev-queue.ccorrupt queue file /.udev/failed_   '!_{D8zG@` hh t(Ṫ4p0(@Жȗ̘`<8l0X ܨ7`̫,xD$(,048,4@ , P $4$0|dxPT\@\X|\`@xT߄4Ld h4 X %S a u   $$o  &\Xooodo%A*aeabi 5TE libudev.so.0.9.1.debugn,.rodata.shstrtab.ARM.extab.dynamic.note.gnu.build-id.eh_frame.gnu.hash.fini.gnu_debuglink.dynsym.gnu.version.rel.dyn.data.rel.ro.gnu.version_r.jcr.dynstr.ARM.attributes.bss.init.rel.plt.got.text.ARM.exidx.fini_array.init_array'$Doc   kodd ox X \\  $xN̟̟pXX :$$$%%&dl'l pl+T olpc-kbdshim-27/common.c000066400000000000000000001412761174753372400153440ustar00rootroot00000000000000/* * common.c -- special touchpad and keyboard support for XO: * - mouse-based scrolling use the XO grab keys * - touchpad and dpad rotation (to match screen rotation) * - user activity monitoring * * Copyright (C) 2009,2010,2011, Paul G. Fox, inspired in places * by code from mouseemu, by Colin Leroy and others. * * 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, see: http://www.gnu.org/licenses */ /* This file contains common code for all versions of olpc-kbdshim, * including a common main(). The various implementions must * provide: * * int setup_input(void) * called for early initialization. * void reinit_activity_monitor(void) * called when the idle timers have been changed * void monitor_input(void) * this is the last thing called from main(), and will * probably never return. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" extern char *optarg; extern int optind, opterr, optopt; /* log to syslog (instead of stderr) */ static int logtosyslog; /* higher values for more debug */ static int debug; /* suppress any actual tranmission or injection of data */ static int noxmit; /* suppress keyboard keys and touchpad activity when in ebook mode */ static int watch_ebook = 1; /* which keys do grabbing? */ #define NGRABKEYS 4 static struct keypress { int code; int pass; int pressed; } grabkey[NGRABKEYS]; static int ngrab; /* command fifo */ static char *command_fifo = "/var/run/olpc-kbdshim_command"; int fifo_fd = -1; /* sysactive path -- where we send user activity/idle events */ static char *sysactive_path = "/var/run/powerevents"; /* list of idle deltas. last timer should stay zero */ int idledeltas[MAXTIMERS + 1] = { 10 * 60, 5 * 60, 5 * 60, 0 }; /* the path to the disable control for the local touchpad. * determined automatically, but can be overridden with -t on the * command line */ char local_tpad_enabled[1024]; /* external commands bound to rotate and brightness keys: * * the rotate command should inform us via our command fifo when * a rotate has happened, so that we can adjust the touchpad and * d-pad rotation. * * the brightness command should accept a single argument of * "up", "down", "min", or "max" (the last two when the alt * modifier is in effect). * * these bindings are done here for convenience, and could be * done anywhere else in the system just as well. binding * them here effectively "steals" them from sugar, which has * internal implementations of rotation and brightness control * which are no longer correct when this package and olpc-powerd * are used. * * the brightness and volume keys appear on F9/F10 and F11/F12. * to accomodate gnome apps, which want full access to the F-keys, * kbdshim will (as of july 2010, see d.l.o. #10213) bind to the * Fn-modified version of those keys as well, which we've arranged * to be KEY_BRIGHTNESSDOWN/UP and KEY_VOLUMEDOWN/UP. * * either of the F9-F12 or KEY_BRIGHTNESS/KEY_VOLUME set of * bindings can be disabled independently, using the following * kbdshim commands: * f9 to disable capture of F9-F12, * F9 to enable capture of F9-F12, * fbv to disable capture of KEY_BRIGHTNESSDOWN/UP and KEYVOLUMEDOWN/UP * FBV to enable capture of KEY_BRIGHTNESSDOWN/UP and KEYVOLUMEDOWN/UP */ static char *brightness_cmd; /* probably "olpc-brightness" */ static char *volume_cmd; /* probably "olpc-volume" */ static char *rotate_cmd; /* probably "olpc-rotate" */ static int reflect_x, reflect_y; /* inverted mouse (e.g., for ebook mode) */ static enum rotation { ROT_NORMAL, ROT_LEFT, ROT_INVERT, ROT_RIGHT } rotation; /* are we in ebook mode */ static int ebook_mode; /* are we scrolling, or not */ static int scrolling; /* last recorded absolute mouse coordinates. * -1 indicates no recorded coords. */ static int last_abs_x = -1; static int last_abs_y = -1; #define SCROLL_QUANTUM 20 #define SCROLL_FILTER_PERCENT 33 static int cumul_x, cumul_y; /* "distance traveled" before we emulate a scroll button event */ static int quantum = SCROLL_QUANTUM; static int ratio = SCROLL_FILTER_PERCENT; /* if true, finger moves scrollbars, instead of window contents */ static int reverse = 1; /* is the D-pad in pointer mode? */ static int dpad_pointer; /* should we capture F9 through F12 for brightness and volume? */ static int use_F9_F12 = 0; /* likewise for F21 through F24 for brightness and volume */ static int use_BRT_VOL = 1; /* product, vendor id for keyboard and touchpad */ struct input_id local_kbd_id = { bustype: BUS_I8042, product: 0xffff, vendor: 0xffff }; struct input_id local_tpad_id = { bustype: BUS_I8042, product: 0xffff, vendor: 0xffff }; /* Axis information for touchscreen */ int tscreen_max[ABS_CNT] = { 0 }; int tscreen_min[ABS_CNT] = { 0 }; int tscreen_fuzz[ABS_CNT] = { 0 }; int tscreen_flat[ABS_CNT] = { 0 }; /* swipe gesture thresholds, etc. */ #define QUEUE_SIZE 512 int swipe_margin; int swipe_trigger_num; int swipe_trigger_dist; int swipe_scroll_quantum; /* output events device */ static int uinp_fd = -1; static int ts_uinp_fd = -1; static char *me; __attribute__((noreturn)) static void usage(void) { fprintf(stderr, "usage: %s [options]\n" " Grab-scroll configuration:\n" " '-g N','-G N' Specify which key(s) are used for grabbing.\n" " A maximum of %d keys can be specified. (Use \"showkey\"\n" " at the console to find the keycode.) With '-g', the\n" " key will be processed normally (in addition to instituting\n" " scrolling). '-G' will cause scrolling but suppress the\n" " key's normal action. Default is '-g 125 -g 126'. (The\n" " \"meta\" keys, labeled as \"grab\" keys on the XO laptop.\n" " Use '-g 0' to suppress grab-scoll support.\n" " '-v' to reverse the relative motion of mouse and window\n" " By default the mouse/finger slides the scrollbars, but\n" " with -v it effectively slides the window instead.\n" " '-q N' to set how far the mouse/finger must move before a\n" " scrolling event is generated (default is %d).\n" " '-n N' If the x or y motion is less than N percent of the\n" " other (default N is %d) then the smaller of the two is\n" " dropped. This reduces sideways jitter when scrolling\n" " vertically,and vice versa.\n" "\n" " Ebook monitoring:\n" " '-e 1|0' to monitor ebook mode, and suppress keyboard and touchpad\n" " inputs when in ebook mode. (defaults to on).\n" "\n" " Touchpad/D-pad rotation:\n" " '-R ' If set, this gives the name of a fifo which\n" " will be created and monitored for the purpose of rotating\n" " and reflecting the touchpad and the D-pad. This can be\n" " used to make their function match the screen's rotation.\n" " This fifo will also receive setup commands related to\n" " user activity monitoring (see below).\n" "\n" " Keyboard/touchpad identification:\n" " '-K BB:VV:PP' Specifies the bustype, vendor, and product id\n" " for the local keyboard. BB, VV, and PP are hex\n" " numbers. ffff can be used as a wildcard for any of them.\n" " Default is '11:ffff:ffff' for the i8042 bus, which will find\n" " the local keyboard. '03:ffff:ffff' finds any USB keyboard.\n" " '-T BB:VV:PP' Same as -K, but for the pointer device.\n" " Only the local keyboard and touchpad will be affected by\n" " rotation commands (i.e., rotate with the screen)\n" " '-t path' Sets path to the sysfs node which can be used to\n" " disable the local touchpad. This path is usually found\n" " automatically, but can be overridden if needed\n" "\n" " User activity monitor:\n" " '-A PATH' Specifies path where user idle/activity events will\n" " be written (default is /var/run/powerevents).\n" "\n" " Key bindings:\n" " '-r rotate-cmd' If set, the command to be run when the XO\n" " 'rotate' button is detected. (Will be passed no arguments.)\n" " '-b brightness-cmd' If set, the command to be run when the XO\n" " brightness keys are used. (Should accept 'min', 'max', 'up'\n" " or 'down' as the sole argument.)\n" " '-V volume-cmd' If set, the command to be run when the XO\n" " volume keys are used. (Should accept 'min', 'max', 'up'\n" " or 'down' as the sole argument.)\n" "\n" " Daemon options:\n" " '-f' to keep program in foreground.\n" " '-l' use syslog, rather than stderr, for messages.\n" " '-s' to run with elevated (sched_fifo) scheduling priority.\n" " '-d' for debugging (repeat for more verbosity).\n" " '-X' don't actually pass on received keystrokes (for debug).\n" "(olpc-kbdshim version %d)\n" , me, NGRABKEYS, SCROLL_QUANTUM, SCROLL_FILTER_PERCENT, VERSION); exit(1); } void report(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (logtosyslog) { vsyslog(LOG_NOTICE, fmt, ap); } fprintf(stderr, "%s: ", me); vfprintf(stderr, fmt, ap); fputc('\n', stderr); } void dbg(int level, const char *fmt, ...) { va_list ap; if (debug < level) return; va_start(ap, fmt); if (logtosyslog) vsyslog(LOG_NOTICE, fmt, ap); fputc(' ', stderr); vfprintf(stderr, fmt, ap); fputc('\n', stderr); } __attribute__((noreturn)) void die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (logtosyslog) { vsyslog(LOG_ERR, fmt, ap); syslog(LOG_ERR, "exiting -- %m"); } fprintf(stderr, "%s: ", me); vfprintf(stderr, fmt, ap); fprintf(stderr, " - %s", strerror(errno)); fputc('\n', stderr); exit(1); } static void write_uinput_event(struct input_event *event) { if (!noxmit && write(uinp_fd, event, sizeof(*event)) < 0) { report("warning: write event failed, t/c/v 0x%x/0x%x/0x%x", event->type, event->code, event->value); } } /* Manage the uinput device */ void inject_uinput_event(unsigned int type, unsigned short code, int value) { struct input_event event; gettimeofday(&event.time, NULL); event.type = type; event.code = code; event.value = value; write_uinput_event(&event); } void deinit_uinput_device(int fd) { if (fd < 0) return; /* Destroy the input device */ if (ioctl(fd, UI_DEV_DESTROY) < 0) die("destroy of uinput dev failed"); /* Close the UINPUT device */ close(fd); fd = -1; } void deinit_both_uinput_devices(void) { deinit_uinput_device(uinp_fd); deinit_uinput_device(ts_uinp_fd); } int setup_uinput(int tscreen) { struct uinput_user_dev uinp; int e, i, avg; int fd; fd = open("/dev/input/uinput", O_WRONLY | O_NDELAY); if (fd < 0) { fd = open("/dev/uinput", O_WRONLY | O_NDELAY); if (fd < 0) { report( "Unable to open either /dev/input/uinput or /dev/uinput"); return -1; } } memset(&uinp, 0, sizeof(uinp)); uinp.id.bustype = BUS_VIRTUAL; uinp.id.vendor = 'p'; uinp.id.product = 'g'; uinp.id.version = 'f'; if (tscreen) { strncpy(uinp.name, "olpc-kbdshim virtual touchscreen", UINPUT_MAX_NAME_SIZE); /* Set up absolute info */ memcpy(uinp.absmax, tscreen_max, sizeof(tscreen_max)); memcpy(uinp.absmin, tscreen_min, sizeof(tscreen_min)); memcpy(uinp.absfuzz, tscreen_fuzz, sizeof(tscreen_fuzz)); memcpy(uinp.absflat, tscreen_flat, sizeof(tscreen_flat)); /* FIXME: assumes all absmin are 0 */ avg = (tscreen_max[ABS_X] + tscreen_max[ABS_Y]) / 2; swipe_margin = avg / 16; swipe_trigger_num = avg / 32; swipe_trigger_dist = avg / 16; swipe_scroll_quantum = avg / 20; e = 0; if (!++e || ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || !++e || ioctl(fd, UI_SET_EVBIT, EV_ABS) < 0 || // touchscreen !++e || ioctl(fd, UI_SET_ABSBIT, ABS_X) < 0 || !++e || ioctl(fd, UI_SET_ABSBIT, ABS_Y) < 0 || !++e || ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X) < 0 || !++e || ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y) < 0 || !++e || ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE) < 0 || !++e || ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID) < 0 || !++e || ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH) < 0 || !++e || write(fd, &uinp, sizeof(uinp)) < 0 || // device !++e || ioctl(fd, UI_DEV_CREATE) < 0) { report("uinput setup failed, step %d", e); return -1; } ts_uinp_fd = fd; dbg(1, "touchscreen uinput device established"); } else { strncpy(uinp.name, "olpc-kbdshim virtual input", UINPUT_MAX_NAME_SIZE); for (i = 0; i <= KEY_UNKNOWN; i++) { if (ioctl(fd, UI_SET_KEYBIT, i) != 0) { report("uinput setup failed, code %d", i); return -1; } } e = 0; if (!++e || ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || // keys !++e || ioctl(fd, UI_SET_EVBIT, EV_REP) < 0 || !++e || ioctl(fd, UI_SET_EVBIT, EV_REL) < 0 || // mouse !++e || ioctl(fd, UI_SET_RELBIT, REL_X) < 0 || !++e || ioctl(fd, UI_SET_RELBIT, REL_Y) < 0 || !++e || ioctl(fd, UI_SET_RELBIT, REL_WHEEL) < 0 || !++e || ioctl(fd, UI_SET_RELBIT, REL_HWHEEL) < 0 || !++e || ioctl(fd, UI_SET_KEYBIT, BTN_LEFT) < 0 || !++e || ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT) < 0 || !++e || ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE) < 0 || !++e || write(fd, &uinp, sizeof(uinp)) < 0 || // device !++e || ioctl(fd, UI_DEV_CREATE) < 0) { report("uinput setup failed, step %d", e); return -1; } uinp_fd = fd; /* disable timer-based autorepeat, see http://dev.laptop.org/ticket/9690 */ inject_uinput_event(EV_REP, REP_DELAY, 0); inject_uinput_event(EV_REP, REP_PERIOD, 0); dbg(1, "uinput device established"); } return 0; } static int match_input_id(struct input_id *id, struct input_id *tmpl) { return id->bustype == tmpl->bustype && (id->vendor == tmpl->vendor || tmpl->vendor == 0xffff) && (id->product == tmpl->product || tmpl->product == 0xffff); } int is_local_kbd(struct input_id *id) { return match_input_id(id, &local_kbd_id); } int is_local_tpad(struct input_id *id) { return match_input_id(id, &local_tpad_id); } void send_a_scroll(int x, int y) { if (reverse) { x = -x; y = -y; } if (x) { dbg(1, "scroll %s", x > 0 ? "left" : "right"); inject_uinput_event(EV_REL, REL_HWHEEL, x < 0 ? 1 : -1); } if (y) { dbg(1, "scroll %s", y > 0 ? "up" : "down"); inject_uinput_event(EV_REL, REL_WHEEL, y < 0 ? -1 : 1); } inject_uinput_event(EV_SYN, SYN_REPORT, 0); } static unsigned char dpad[] = { KEY_KP8, KEY_KP6, KEY_KP2, KEY_KP4, KEY_KP8, KEY_KP6, KEY_KP2, KEY_KP4}; void handle_dpad_pointer(struct input_event *ev) { int xmot, ymot, numdirs; static int dpad_up_pressed; // D-pad directions currently pushed? static int dpad_down_pressed; static int dpad_left_pressed; static int dpad_right_pressed; switch(ev->code) { case KEY_KP8: dpad_up_pressed = ev->value; break; case KEY_KP2: dpad_down_pressed = ev->value; break; case KEY_KP4: dpad_left_pressed = ev->value; break; case KEY_KP6: dpad_right_pressed = ev->value; break; } xmot = ymot = numdirs = 0; if (dpad_up_pressed) { dbg(3, "dpad up pressed"); ymot -= 1; numdirs++; } if (dpad_down_pressed) { dbg(3, "dpad down pressed"); ymot += 1; numdirs++; } if (dpad_left_pressed) { dbg(3, "dpad left pressed"); xmot -= 1; numdirs++; } if (dpad_right_pressed) { dbg(3, "dpad right pressed"); xmot += 1; numdirs++; } ev->type = EV_REL; if (xmot) { ev->code = REL_X; ev->value = xmot * (ymot ? 2 : 4); dbg(3, "dpad X %d", ev->value); if (!noxmit && write(uinp_fd, ev, sizeof(*ev)) < 0) { report("uinput write failed: %s", strerror(errno)); } } if (ymot) { ev->code = REL_Y; ev->value = ymot * (xmot ? 2 : 4); dbg(3, "dpad Y %d", ev->value); if (!noxmit && write(uinp_fd, ev, sizeof(*ev)) < 0) { report("uinput write failed: %s", strerror(errno)); } } if (xmot || ymot) inject_uinput_event(EV_SYN, SYN_REPORT, 0); } int keyboard_event_worker(struct input_event *ev, int is_local) { int i; int pass = 1; static int altdown, ctrldown; dbg(3, "keyboard evtype %d code %d value %d", ev->type, ev->code, ev->value); if (ev->type == EV_KEY) { if (is_local && ebook_mode) { switch(ev->code) { case KEY_KP1: // 4 game keys case KEY_KP3: case KEY_KP7: case KEY_KP9: case KEY_KP4: // 4 dpad arrow keys case KEY_KP2: case KEY_KP6: case KEY_KP8: case KEY_SWITCHVIDEOMODE: // rotate button break; default: dbg(3, "ebook: discarding keyboard"); return 0; } } for (i = 0; i < ngrab; i++) { if (ev->code == grabkey[i].code) { /* keep track of how many grab keys are down */ if (ev->value) { if (grabkey[i].pressed++ == 0) scrolling++; } else { grabkey[i].pressed = 0; if (--scrolling == 0) cumul_x = cumul_y = 0; } pass = grabkey[i].pass; dbg(2, "scrolling %d", scrolling); break; } } } /* implement arrow key grab-scrolling */ if (scrolling && ev->value) { switch(ev->code) { case KEY_UP: send_a_scroll( 0,-1); pass = 0; break; case KEY_DOWN: send_a_scroll( 0, 1); pass = 0; break; case KEY_LEFT: send_a_scroll(-1, 0); pass = 0; break; case KEY_RIGHT: send_a_scroll( 1, 0); pass = 0; break; } } if (is_local) { i = 0; switch(ev->code) { case KEY_KP4: i++; /* rotate the dpad on the screen bezel */ case KEY_KP2: i++; case KEY_KP6: i++; case KEY_KP8: ev->code = dpad[rotation + i]; if (dpad_pointer) { handle_dpad_pointer(ev); pass = 0; } break; case KEY_SWITCHVIDEOMODE: // rotate button dbg(3, "might rotate"); if (rotate_cmd && ev->value == 1) { /* ignore value 2 (repeats) */ // command given, so don't pass the keystroke through. pass = 0; // if absolute, we can check for presence cheaply if (*rotate_cmd != '/' || access(rotate_cmd, X_OK) == 0) { dbg(1, "invoking %s", rotate_cmd); if (system(rotate_cmd) == 0) ; } } break; case KEY_F9: // brightness down key (or minimize, with alt) case KEY_F10: // brightness up key (or maximize, with alt) dbg(3, "F9 or F10, %d", use_F9_F12); if (!use_F9_F12) break; goto brightness; case KEY_BRIGHTNESSDOWN: case KEY_BRIGHTNESSUP: dbg(3, "BRTD or BRTU, %d", use_BRT_VOL); if (!use_BRT_VOL) break; brightness: dbg(3, "might brighten"); if (brightness_cmd && ev->value) { char cmd[256]; char *arg; int kreduce = (ev->code == KEY_F9 || ev->code == KEY_BRIGHTNESSDOWN); // command given, so don't pass the keystroke through. pass = 0; // if absolute, we can check for presence cheaply if (*brightness_cmd != '/' || access(brightness_cmd, X_OK) == 0) { if (altdown) { arg = kreduce ? "min" : "max"; } else if (ctrldown) { arg = kreduce ? "mono" : "color"; } else { arg = kreduce ? "down" : "up"; } if (snprintf(cmd, sizeof(cmd), "%s %s", brightness_cmd, arg) <= sizeof(cmd)) { dbg(1, "invoking %s", cmd); if (system(cmd) == 0) ; } } } break; case KEY_F11: // volume down key (or minimize, with alt) case KEY_F12: // volume up key (or maximize, with alt) dbg(3, "F11 or F12, %d", use_F9_F12); if (!use_F9_F12) break; goto volume; case KEY_VOLUMEDOWN: case KEY_VOLUMEUP: dbg(3, "VOLD or VOLU, %d", use_BRT_VOL); if (!use_BRT_VOL) break; volume: dbg(3, "might adjust volume"); if (volume_cmd && ev->value) { char cmd[256]; char *arg; int kreduce = (ev->code == KEY_F11 || ev->code == KEY_VOLUMEDOWN); // command given, so don't pass the keystroke through. pass = 0; // if absolute, we can check for presence cheaply if (*volume_cmd != '/' || access(volume_cmd, X_OK) == 0) { if (altdown) { arg = kreduce ? "min" : "max"; } else { arg = kreduce ? "down" : "up"; } if (snprintf(cmd, sizeof(cmd), "%s %s", volume_cmd, arg) <= sizeof(cmd)) { dbg(1, "invoking %s", cmd); if (system(cmd) == 0) ; } } } break; case KEY_LEFTALT: case KEY_RIGHTALT: if (ev->value == 0) altdown--; else if (ev->value == 1) altdown++; else /* ignore repeats with ev->value == 2 */ ; if (altdown > 2) altdown = 2; // safety else if (altdown < 0) altdown = 0; // safety dbg(3, "altdown now %d", altdown); break; case KEY_LEFTCTRL: case KEY_RIGHTCTRL: if (ev->value == 0) ctrldown--; else if (ev->value == 1) ctrldown++; else /* ignore repeats with ev->value == 2 */ ; if (ctrldown > 2) ctrldown = 2; // safety else if (ctrldown < 0) ctrldown = 0; // safety dbg(3, "ctrldown now %d", ctrldown); break; } } dbg(3, "keyboard sending"); if (pass && !noxmit && write(uinp_fd, ev, sizeof(*ev)) < 0) { report("uinput write failed: %s", strerror(errno)); } dbg(3, "keyboard_event returning %d", !!(ev->value)); return !!(ev->value); /* indicates press */ } static void handle_touchpad_scroll(unsigned int type, unsigned short code, int value) { dbg(3, "touchpad scrolling"); if (type == EV_REL) { if (code == REL_X) cumul_x += value; else if (code == REL_Y) cumul_y += value; } else if (type == EV_SYN) { /* emit our current scroll input */ int x = 0, y = 0; if (abs(cumul_y) > quantum) { if (abs(cumul_x) < ratio * abs(cumul_y) / 100) cumul_x = 0; y = cumul_y; cumul_y = 0; } if (abs(cumul_x) > quantum) { if (abs(cumul_y) < ratio * abs(cumul_x) / 100) cumul_y = 0; x = cumul_x; cumul_x = 0; } send_a_scroll(x, y); } } static void handle_rotation(unsigned short *code, int *value, int is_local) { unsigned short _code = *code; int _value = *value; /* only rotate/flip the local touchpad */ if (is_local) { if ((reflect_x && _code == REL_X) || (reflect_y && _code == REL_Y)) { *value = -_value; } switch(rotation) { case ROT_NORMAL: break; case ROT_RIGHT: if (_code == REL_Y) { *code = REL_X; } else if (_code == REL_X) { *code = REL_Y; *value = -_value; } break; case ROT_LEFT: if (_code == REL_Y) { *code = REL_X; *value = -_value; } else if (_code == REL_X) { *code = REL_Y; } break; case ROT_INVERT: *value = -_value; break; } } } void abs_touchpad_event_worker(struct input_event *ev, int is_local) { unsigned short new_code = ev->code; unsigned int new_type = ev->type; int rel_value = ev->value; dbg(3, "touchpad abs evtype %d code %d, rot is %d", ev->type, ev->code, rotation); if (ev->type == EV_KEY) { switch (ev->code) { case BTN_TOUCH: if (ev->value == 0) { dbg(3, "reset last_abs"); last_abs_x = -1; last_abs_y = -1; return; } break; default: /* passthrough clicks */ dbg(3, "abs touchpad sending click"); write_uinput_event(ev); return; } } if (ev->type != EV_ABS && ev->type != EV_SYN) return; if (ev->type == EV_ABS) { /* Translate to a relative movement */ if (ev->code == ABS_X && last_abs_x == -1) { last_abs_x = ev->value; return; } else if (ev->code == ABS_Y && last_abs_y == -1) { last_abs_y = ev->value; return; } /* At this point we're dealing with a relative movement. */ switch (ev->code) { case ABS_X: rel_value = ev->value - last_abs_x; new_code = REL_X; last_abs_x = ev->value; break; case ABS_Y: rel_value = ev->value - last_abs_y; new_code = REL_Y; last_abs_y = ev->value; break; default: dbg(3, "Unrecognised code"); return; } new_type = EV_REL; handle_rotation(&new_code, &rel_value, is_local); } if (scrolling) { handle_touchpad_scroll(new_type, new_code, rel_value); return; } /* Pass through SYNs now */ if (ev->type == EV_SYN) { write_uinput_event(ev); return; } /* Otherwise inject our translated event */ inject_uinput_event(new_type, new_code, rel_value); } void touchpad_event_worker(struct input_event *ev, int is_local) { dbg(3, "touchpad evtype %d code %d, rot is %d", ev->type, ev->code, rotation); if (is_local && ebook_mode) { dbg(3, "ebook: discarding touchpad"); return; } if (ev->type == EV_REL) handle_rotation(&ev->code, &ev->value, is_local); if (scrolling && ev->type != EV_KEY) { // i.e., EV_REL or EV_SYN handle_touchpad_scroll(ev->type, ev->code, ev->value); } else { /* passthrough */ dbg(3, "touchpad sending"); write_uinput_event(ev); } dbg(3, "touchpad_event done", !!(ev->value)); } #define TS_MAX 32767 void touchscreen_event_rotate(struct input_event *ev) { if (ev->type == EV_ABS) { if (reflect_x && (ev->code == ABS_X || ev->code == ABS_MT_POSITION_X)) { ev->value = tscreen_max[ABS_X] - ev->value; } if (reflect_y && (ev->code == ABS_X || ev->code == ABS_MT_POSITION_Y)) { ev->value = tscreen_max[ABS_Y] - ev->value; } switch (rotation) { case ROT_NORMAL: break; case ROT_LEFT: switch (ev->code) { case ABS_X: ev->code = ABS_Y; ev->value = ev->value * tscreen_max[ABS_Y] / tscreen_max[ABS_X]; break; case ABS_MT_POSITION_X: ev->code = ABS_MT_POSITION_Y; ev->value = ev->value * tscreen_max[ABS_Y] / tscreen_max[ABS_X]; break; case ABS_Y: ev->code = ABS_X; ev->value = (tscreen_max[ABS_Y] - ev->value) * tscreen_max[ABS_X] / tscreen_max[ABS_Y]; break; case ABS_MT_POSITION_Y: ev->code = ABS_MT_POSITION_X; ev->value = (tscreen_max[ABS_Y] - ev->value) * tscreen_max[ABS_X] / tscreen_max[ABS_Y]; break; } break; case ROT_RIGHT: switch (ev->code) { case ABS_X: ev->code = ABS_Y; ev->value = (tscreen_max[ABS_X] - ev->value) * tscreen_max[ABS_Y] / tscreen_max[ABS_X]; break; case ABS_MT_POSITION_X: ev->code = ABS_MT_POSITION_Y; ev->value = (tscreen_max[ABS_X] - ev->value) * tscreen_max[ABS_Y] / tscreen_max[ABS_X]; break; case ABS_Y: ev->code = ABS_X; ev->value = ev->value * tscreen_max[ABS_X] / tscreen_max[ABS_Y]; break; case ABS_MT_POSITION_Y: ev->code = ABS_MT_POSITION_X; ev->value = ev->value * tscreen_max[ABS_X] / tscreen_max[ABS_Y]; break; break; } break; case ROT_INVERT: switch (ev->code) { case ABS_X: case ABS_MT_POSITION_X: ev->value = tscreen_max[ABS_X] - ev->value; break; case ABS_Y: case ABS_MT_POSITION_Y: ev->value = tscreen_max[ABS_Y] - ev->value; break; } break; } } } void touchscreen_event_worker(struct input_event *ev, int is_local) { static struct input_event queue[QUEUE_SIZE]; static int queue_pos = 0; static enum swipe_state { NO_SWIPE, /* looking for swipe start, no current touch */ NO_SWIPE_TOUCHED,/* touch which is not a swipe, ignore */ POTENTIAL_SWIPE, /* swipe maybe started; see 'swipe edge' */ SWIPING, /* swipe in progress; see 'swipe edge' for direction */ DONE_SWIPING, /* done processing a swipe, wait for touch up */ } swipe_state = NO_SWIPE; static enum { NONE, FROM_TOP, FROM_BOTTOM, /* scroll wheel swipes */ FROM_LEFT, FROM_RIGHT, /* wm swipes */ } swipe_dir = NONE; static int wm_pos = 2; /* start at xo home screen */ static int last_x, last_y; int i, good; dbg(3, "pre: touchscreen evtype %d code %d value %d", ev->type, ev->code, ev->value); touchscreen_event_rotate(ev); dbg(3, "post: touchscreen evtype %d code %d value %d", ev->type, ev->code, ev->value); /* queue events */ memcpy(&(queue[queue_pos]), ev, sizeof(*ev)); queue_pos = (queue_pos + 1) % QUEUE_SIZE; // queue up until we get a SYN if (ev->type != EV_SYN) return; if (ev->code != SYN_REPORT) return; /* scan buffer, look for touches starting at the edges */ if (swipe_state == NO_SWIPE) { swipe_dir = NONE; good = 0; for (i=0; i=tscreen_max[ABS_X]-swipe_margin) swipe_dir = FROM_RIGHT; break; case ABS_Y: last_y = queue[i].value; if (queue[i].value<=tscreen_min[ABS_Y]+swipe_margin) swipe_dir = FROM_TOP; if (queue[i].value>=tscreen_max[ABS_Y]-swipe_margin) swipe_dir = FROM_BOTTOM; break; default: break; } break; case EV_KEY: if (queue[i].code == BTN_TOUCH && queue[i].value) good = 1; break; default: break; } } if (good) { if (swipe_dir == NONE) swipe_state = NO_SWIPE_TOUCHED; else { swipe_state = POTENTIAL_SWIPE; report("Starting potential swipe: %d", swipe_dir); return; // don't pass this through yet. } } } else if (swipe_state == NO_SWIPE_TOUCHED || swipe_state == DONE_SWIPING) { /* just look for touch release. */ good=0; for (i=0; i SWIPE_TRIGGER_DIST * touch up * and then see if this is a real swipe. */ int dist_x=0, dist_y=0; // start at queue_pos-2; queue_pos-1 is a SYN_REPORT for (i=queue_pos-2; i>=0; i--) { if (queue[i].type==EV_SYN && queue[i].code==SYN_REPORT) break; if (queue[i].type==EV_ABS && queue[i].code==ABS_X) dist_x = last_x - queue[i].value; if (queue[i].type==EV_ABS && queue[i].code==ABS_Y) dist_y = last_y - queue[i].value; if (queue[i].type==EV_KEY && queue[i].code==BTN_TOUCH && !queue[i].value) { report("Not a swipe (lifted): pos %d x %d y %d", queue_pos, dist_x, dist_y); swipe_state = NO_SWIPE; } } if (dist_x < 0) dist_x = -dist_x; if (dist_y < 0) dist_y = -dist_y; if (swipe_state == POTENTIAL_SWIPE && (dist_x > swipe_trigger_dist || dist_y > swipe_trigger_dist)) { // is this a swipe? // XXX should check that swipe is "straight enough"? report("It's a swipe: pos %d x %d y %d", queue_pos, dist_x, dist_y); swipe_state = SWIPING; queue_pos = 0; // yeah, it's a swipe, no need to keep buffering } if (swipe_state == POTENTIAL_SWIPE && queue_pos > swipe_trigger_num) { // held finger down too long w/o travelling far enough, no swipe report("Not a swipe (too long): pos %d x %d y %d", queue_pos, dist_x, dist_y); swipe_state = NO_SWIPE; } if (swipe_state != NO_SWIPE) return; } else if (swipe_state == SWIPING) { if (swipe_dir == FROM_LEFT || swipe_dir == FROM_RIGHT) { if (swipe_dir == FROM_LEFT && wm_pos > 0) wm_pos--; if (swipe_dir == FROM_RIGHT && wm_pos < 3) wm_pos++; inject_uinput_event(EV_KEY, KEY_F1 + wm_pos, 1); inject_uinput_event(EV_SYN, SYN_REPORT, 0); inject_uinput_event(EV_KEY, KEY_F1 + wm_pos, 0); inject_uinput_event(EV_SYN, SYN_REPORT, 0); queue_pos = 0; // throw away packet. swipe_state = DONE_SWIPING; return; } else if (swipe_dir == FROM_TOP || swipe_dir == FROM_BOTTOM) { // look for ABS_Y int y = last_y, dist_y; for (i=0; i swipe_scroll_quantum || dist_y < -swipe_scroll_quantum) { last_y = y; send_a_scroll(0, dist_y); } queue_pos = 0; return; } // should never get here. queue_pos = 0; // throw away packet. swipe_state = DONE_SWIPING; return; } /* passthrough */ dbg(3, "touchscreen sending %d", queue_pos); for (i=0; i= 0 || axis < ABS_CNT)) return; report("Configuring touchscreen axis %d: min %d max %d fuzz %d flat %d", axis, info->minimum, info->maximum, info->fuzz, info->flat); tscreen_max[axis] = info->maximum; tscreen_min[axis] = info->minimum; tscreen_fuzz[axis] = info->fuzz; tscreen_flat[axis] = info->flat; } void ebook_event_worker(struct input_event *ev) { static int can_disable = 1; // assume yes, for now dbg(3, "ebook evtype %d code %d value %d", ev->type, ev->code, ev->value); if (watch_ebook && ev->type == EV_SW && ev->code == SW_TABLET_MODE) { ebook_mode = ev->value; if (can_disable) { int fd; fd = open(local_tpad_enabled, O_RDWR|O_NONBLOCK); if (fd >= 0) { if (write(fd, ebook_mode ? "0":"1", 1) < 0) report("warning: tpad-enabled write failed"); close(fd); } else { report("Unable to open %s\n", local_tpad_enabled); can_disable = 0; } } } } void command_worker(char *command) { int n; unsigned int a, b, c; dbg(1, "command is '%s'\n", command); switch (command[0]) { case 'x': reflect_x = 0; break; case 'y': reflect_y = 0; break; case 'z': reflect_x = 0; reflect_y = 0; break; case 'X': reflect_x = 1; break; case 'Y': reflect_y = 1; break; case 'Z': reflect_x = 1; reflect_y = 1; break; case 'n': rotation = ROT_NORMAL; break; case 'r': rotation = ROT_RIGHT; break; case 'i': rotation = ROT_INVERT; break; case 'l': rotation = ROT_LEFT; break; case 'I': n = sscanf(command, "I %u %u %u", &a, &b, &c); if (n < 1) { a = b = c = 0; } else { if (n < 2 || b < a + 1) b = a + 1; if (n < 3 || c < b + 1) c = b + 1; } { static unsigned int old_a, old_b, old_c; if (a != old_a || b != old_b || c != old_c) { report("idle timers changed to %d %d %d", a, b, c); } else { dbg(1,"idle timers set to %d %d %d", a, b, c); } old_a = a; old_b = b; old_c = c; } idledeltas[3] = 0; /* always, so our last sleep is untimed */ idledeltas[2] = c - b; idledeltas[1] = b - a; idledeltas[0] = a; reinit_activity_monitor(); break; case 'd': dpad_pointer = 0; break; case 'D': dpad_pointer = 1; break; case 'f': case 'F': /* F9/f9 to enable/disable capture of F9-F12, * FBV/fbv to enable/disable capture of Fn-BRt, Fn-Vol keys */ if (!strncasecmp(command, "F9", 2)) { use_F9_F12 = (command[0] == 'F'); } else if (!strncasecmp(command, "FBV", 3)) { use_BRT_VOL = (command[0] == 'F'); } dbg(1,"fkey, now: use_F9_F12 %d, use_BRT_VOL %d", use_F9_F12, use_BRT_VOL); break; default: rotation = ROT_NORMAL; reflect_x = 0; reflect_y = 0; break; } dbg(1, "command done, rot/refl_x/refl_y is %d/%d/%d", rotation, reflect_x, reflect_y); } void send_event(char *e) { int fd, n; char evtbuf[128]; fd = open(sysactive_path, O_RDWR|O_NONBLOCK); if (fd >= 0) { n = snprintf(evtbuf, 128, "%s %d\n", e, (int)time(0)); n = write(fd, evtbuf, n); close(fd); } } int init_fifo(char *fifo_node) { int fd; struct stat sbuf; #define fifomode 0622 /* allow anyone to adjust the rotation */ dbg(1, "initializing fifo %s", fifo_node); if (mkfifo(fifo_node, fifomode)) { if (errno != EEXIST) { report("mkfifo of %s failed", fifo_node); return -1; } /* the path exists. is it a fifo? */ if (stat(fifo_node, &sbuf) < 0) { report("stat of %s failed", fifo_node); return -1; } /* if not, remove and recreate */ if (!S_ISFIFO(sbuf.st_mode)) { unlink(fifo_node); if (mkfifo(fifo_node, fifomode)) { report("recreate of %s failed", fifo_node); return -1; } } } /* mkfifo was affected by umask */ if (chmod(fifo_node, fifomode)) { report("chmod of %s to 0%o failed", fifo_node, fifomode); return -1; } /* open for read/write, since writers are itinerant */ fd = open(fifo_node, O_RDWR); if (fd < 0) { report("open %s failed", fifo_node); return -1; } dbg(1, "done with unix fifo init", fifo_node); return fd; } void deinit_fifo(void) { if (fifo_fd >= 0) { close(fifo_fd); fifo_fd = -1; } unlink(command_fifo); } void sighandler(int sig) { deinit_both_uinput_devices(); deinit_fifo(); die("got signal %d", sig); } int main(int argc, char *argv[]) { int sched_realtime = 0; unsigned short b, v, p; char *cp, *eargp; int c; int foreground = 0; me = argv[0]; cp = strrchr(argv[0], '/'); if (cp) me = cp + 1; while ((c = getopt(argc, argv, "flsdXve:q:n:K:T:t:g:G:R:A:r:b:V:")) != -1) { switch (c) { /* daemon options */ case 'f': foreground = 1; break; case 'l': logtosyslog = 1; openlog(me, LOG_PID, LOG_NOTICE); break; case 's': sched_realtime = 1; break; case 'd': debug++; break; case 'X': noxmit = 1; break; /* algorithmic tuning */ case 'v': reverse = 0; break; case 'q': quantum = strtol(optarg, &eargp, 10); if (*eargp != '\0' || quantum <= 0) usage(); break; case 'n': ratio = strtol(optarg, &eargp, 10); if (*eargp != '\0' || ratio <= 0) usage(); break; case 'K': if (sscanf(optarg, "%hx:%hx:%hx", &b, &v, &p) != 3) usage(); local_kbd_id.bustype = b; local_kbd_id.vendor = v; local_kbd_id.product = p; break; case 'T': if (sscanf(optarg, "%hx:%hx:%hx", &b, &v, &p) != 3) usage(); local_tpad_id.bustype = b; local_tpad_id.vendor = v; local_tpad_id.product = p; break; case 't': strncpy(local_tpad_enabled, optarg, sizeof(local_tpad_enabled)); break; case 'g': case 'G': if (ngrab >= NGRABKEYS) { fprintf(stderr, "%s: too many grab keys specified, %d max\n", me, NGRABKEYS); exit(1); } grabkey[ngrab].code = strtol(optarg, &eargp, 10); if (*eargp != '\0') usage(); grabkey[ngrab].pass = (c == 'g'); ngrab++; break; case 'e': watch_ebook = atoi(optarg); break; case 'R': command_fifo = optarg; break; case 'A': sysactive_path = optarg; break; case 'r': rotate_cmd = optarg; break; case 'b': brightness_cmd = optarg; break; case 'V': volume_cmd = optarg; break; default: usage(); break; } } if (optind < argc) { report("found non-option argument(s)"); usage(); } /* default to XO grab keys, with passthrough. since they're * just modifiers, nothing will happen with them unless X * wants it to. */ if (ngrab == 0) { grabkey[0].code = KEY_LEFTMETA; grabkey[0].pass = 1; grabkey[1].code = KEY_RIGHTMETA; grabkey[1].pass = 1; ngrab = 2; } report("starting %s version %d", me, VERSION); if (!setup_input()) die("%s: unable to find input devices", me); /* initialize uinput, if needed */ if (!noxmit) { if (system("/sbin/modprobe uinput") == 0) sleep(1); if (setup_uinput(0) < 0) die("unable to find uinput device"); } atexit(deinit_both_uinput_devices); atexit(deinit_fifo); if (command_fifo) { fifo_fd = init_fifo(command_fifo); if (fifo_fd < 0) report("no fifo for commands"); } signal(SIGTERM, sighandler); signal(SIGHUP, sighandler); signal(SIGINT, sighandler); signal(SIGQUIT, sighandler); signal(SIGABRT, sighandler); signal(SIGUSR1, sighandler); signal(SIGUSR2, sighandler); if (sched_realtime) { struct sched_param sparam; int min, max; /* first, lock down all our memory */ long takespace[1024]; memset(takespace, 0, sizeof(takespace)); /* force paging */ if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) die("unable to mlockall"); /* then, raise our scheduling priority */ min = sched_get_priority_min(SCHED_FIFO); max = sched_get_priority_max(SCHED_FIFO); sparam.sched_priority = (min + max)/2; /* probably always 50 */ if (sched_setscheduler(0, SCHED_FIFO, &sparam)) die("unable to set SCHED_FIFO"); report("memory locked, scheduler priority set"); } if (!foreground) { if (daemon(0, 0) < 0) die("failed to daemonize"); } reinit_activity_monitor(); monitor_input(); return 0; } olpc-kbdshim-27/common.h000066400000000000000000000045671174753372400153520ustar00rootroot00000000000000/* * common.c -- special touchpad and keyboard support for XO: * - mouse-based scrolling use the XO grab keys * - touchpad and dpad rotation (to match screen rotation) * - user activity monitoring * * Copyright (C) 2009,2010,2011, Paul G. Fox, inspired in places * by code from mouseemu, by Colin Leroy and others. * * 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, see: http://www.gnu.org/licenses */ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) /* how many /dev/input/eventX devices to check */ #define EVENTDEVICES 20 #define MAXTIMERS 3 /* list of idle deltas. last entry will always be zero */ extern int idledeltas[]; /* control commands will appear on this fd */ extern int fifo_fd; extern char local_tpad_enabled[1024]; /* provided by common.c */ void report(const char *fmt, ...); void dbg(int level, const char *fmt, ...); void die(const char *fmt, ...); void inject_uinput_event(unsigned int type, unsigned short code, int value); void deinit_uinput_device(int); void deinit_both_uinput_devices(void); int setup_uinput(int); int is_local_kbd(struct input_id *id); int is_local_tpad(struct input_id *id); void send_a_scroll(int x, int y); void handle_dpad_pointer(struct input_event *ev); int keyboard_event_worker(struct input_event *ev, int is_local); void touchpad_event_worker(struct input_event *ev, int is_local); void abs_touchpad_event_worker(struct input_event *ev, int is_local); void touchscreen_event_worker(struct input_event *ev, int is_local); void ebook_event_worker(struct input_event *ev); void touchscreen_config_axis(int axis, struct input_absinfo *info); void command_worker(char *command); void send_event(char *e); int init_fifo(char *fifo_node); void deinit_fifo(void); void sighandler(int sig); /* called from common.c */ int setup_input(void); void reinit_activity_monitor(void); void monitor_input(void); olpc-kbdshim-27/mock-wrapper000077500000000000000000000004151174753372400162320ustar00rootroot00000000000000#!/bin/bash # wraps mock so that it gives a proper error return if it fails. set -o pipefail ( /usr/bin/mock "$@" 2>&1 | tee rpms/mock.out ) || exit $? # now grep through mock.out to see if an ERROR was flagged. grep -q "^ERROR:" rpms/mock.out && exit 1 # fail! exit 0 olpc-kbdshim-27/olpc-brightness000077500000000000000000000062321174753372400167310ustar00rootroot00000000000000#!/bin/sh # adjust brightness of the OLPC XO backlight. # note that brightness is also adjusted automatically by powerd, # for idle screen dimming and for ambient-light backlight # control. usage() { echo "usage: ${0##*/} [up|down|max|min|<0-15>|color|mono]" exit 1; } LEVEL=/sys/class/backlight/dcon-bl/brightness # assume DCON if [ ! -e $LEVEL -a -d /sys/class/backlight ] then # but then choose the first backlight we find BACKLIGHT=$(ls /sys/class/backlight | sed 1q ) if [ "$BACKLIGHT" ] then # assume levels of 0-15 for now -- use max_brightness later LEVEL=/sys/class/backlight/$BACKLIGHT/brightness fi fi if [ -e /sys/devices/platform/dcon/monochrome ] then MONO_MODE=/sys/devices/platform/dcon/monochrome elif [ -e /sys/devices/platform/dcon/output ] then MONO_MODE=/sys/devices/platform/dcon/output fi set_mono() { test "$MONO_MODE" && echo 1 > $MONO_MODE } set_color() { test "$MONO_MODE" && echo 0 > $MONO_MODE } MONO_LOCKDIR=/tmp/olpc-brightness set_mono_mode() { # create a directory to hold our flag, so that anyone # can create or delete it. mkdir -p $MONO_LOCKDIR chmod a+w $MONO_LOCKDIR touch $MONO_LOCKDIR/mono_lock } clear_mono_mode() { rm -f $MONO_LOCKDIR/mono_lock } is_mono_mode() { test -e $MONO_LOCKDIR/mono_lock } set_bright() { echo $1 > $LEVEL } read curbright < $LEVEL case $1 in # no argument, just report current value "") echo $curbright ;; # set a numeric level. not used much [0-9]|1[0-5]) set_bright $1 ;; # the next three options set, clear, and test "monochrome # mode" in this mode, the screen is kept in monochrome mode # all the time. otherwise, it's set to color when the # backlight is on, and to monochrome when it's off (i.e., at # brightness 0) color) clear_mono_mode set_color ;; mono) set_mono set_mono_mode ;; is_mono) is_mono_mode || exit 0 exit 1 ;; # used by powerd, to avoid knowing details of "monochrome mode" maybe_color) is_mono_mode || set_color ;; # these two are typically invoked with alt-brightness up/down max|high) is_mono_mode || set_color set_bright 15 ;; min|low) set_bright 0 set_mono ;; # the next two are the standard brighten/dim operations. # dimming to 0 changes from color to monochrome, and # brightening always switches to color, unless we're in # "monochrome mode". up) newbright=$(( curbright + 1 )) if [ $newbright -gt 15 ] then newbright=15 fi is_mono_mode || set_color set_bright $newbright ;; down) newbright=$(( curbright - 1 )) if [ $newbright -le 0 ] then set_mono set_bright 0 else set_bright $newbright fi ;; *) usage ;; esac olpc-kbdshim-27/olpc-kbdshim-hal.c000066400000000000000000000277121174753372400171700ustar00rootroot00000000000000/* * olpc-kbdshim-hal.c: -- special touchpad and keyboard support for XO: * - mouse-based scrolling use the XO grab keys * - touchpad and dpad rotation (to match screen rotation) * - user activity monitoring * * Copyright (C) 2009,2010,2011, Paul G. Fox, inspired in places * by code from mouseemu, by Colin Leroy and others. * * 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, see: http://www.gnu.org/licenses */ /* * This hal support skeleton of this program program is based heavily on: * addon-input.c : Listen to key events and modify hal device objects * * Copyright (C) 2005 David Zeuthen, * Copyright (C) 2005 Ryan Lortie * Copyright (C) 2006 Matthew Garrett * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" /* are we in user-idle state? */ static int idleness; static int wasidle; static int idle_delta; int setup_input() { return 1; } static int keyboard_event (GIOChannel *channel, int is_local) { struct input_event ev[1]; gsize read_bytes; GError *gerror = NULL; if (g_io_channel_read_chars (channel, (gchar*)ev, sizeof(ev), &read_bytes, &gerror) != G_IO_STATUS_NORMAL) { die("bad read from keyboard"); } if (read_bytes != sizeof(ev)) { dbg(1, "short read of ev (%d)", read_bytes); return 0; } return keyboard_event_worker(ev, is_local); } static void touchpad_event(GIOChannel *channel, int is_local) { struct input_event ev[1]; gsize read_bytes; GError *gerror = NULL; if (g_io_channel_read_chars (channel, (gchar*)ev, sizeof(ev), &read_bytes, &gerror) != G_IO_STATUS_NORMAL) { die("bad read from pointer"); } if (read_bytes != sizeof(ev)) { report("short read of ev (%d)", read_bytes); return; } touchpad_event_worker(ev, is_local); } static gboolean indicate_idleness(gpointer data) { static char useridle[] = "useridleX"; useridle[8] = idleness + '1'; if (idleness < MAXTIMERS) idleness++; send_event(useridle); wasidle = 1; if (idledeltas[idleness]) idle_delta = g_timeout_add_seconds( idledeltas[idleness], indicate_idleness, 0); else idle_delta = 0; return FALSE; } void reinit_activity_monitor(void) { wasidle = 1; idleness = 0; if (idle_delta) g_source_remove(idle_delta); idle_delta = g_timeout_add_seconds( idledeltas[idleness], indicate_idleness, 0); } static void indicate_activity(void) { if (wasidle) send_event("useractive"); reinit_activity_monitor(); wasidle = 0; } static void get_command(GIOChannel *channel) { GError *gerror = NULL; GIOStatus s; static GString *cmd; dbg(1, "reading from command fifo"); if (!cmd) cmd = g_string_new(""); s = g_io_channel_read_line_string (channel, cmd, 0, &gerror); if (s != G_IO_STATUS_NORMAL) { if (s == G_IO_STATUS_AGAIN) { return; } die("read from command fifo"); } command_worker(cmd->str); } /* we must use this kernel-compatible implementation */ #define BITS_PER_LONG (sizeof(long) * 8) #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) #define OFF(x) ((x)%BITS_PER_LONG) #define BIT(x) (1UL<> OFF(bit)) & 1) #define IS_KBD 1 #define IS_POINTER 2 #define IS_LOCAL 4 #define IS_CMDFIFO 8 static LibHalContext *ctx = NULL; static GMainLoop *gmain = NULL; static GHashTable *inputs = NULL; static GList *devices = NULL; static gboolean event_io (GIOChannel *channel, GIOCondition condition, gpointer gdata) { unsigned int info = (unsigned int) gdata; int pressed = 0; dbg(2, "event_io called, info is 0x%x, (%d)", info, !!(info & IS_LOCAL)); if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) return FALSE; if (info & IS_KBD) { pressed |= keyboard_event(channel, !!(info & IS_LOCAL)); dbg(3, "kbd activity (%d)", !!(info & IS_LOCAL)); } if (info & IS_POINTER) { touchpad_event(channel, !!(info & IS_LOCAL)); pressed = 1; dbg(3, "pointer activity (%d)", !!(info & IS_LOCAL)); } if (info & IS_CMDFIFO) { dbg(3, "got command"); get_command(channel); } if (pressed) indicate_activity(); return TRUE; } static void add_device (LibHalContext *ctx, const char *udi, const LibHalPropertySet *properties) { int eventfp; GIOChannel *channel; unsigned int info; const char* device_file; struct input_id id; unsigned long bit[NBITS(EV_MAX)]; dbg(1, "add_device called"); device_file = libhal_ps_get_string (properties, "input.device"); if (device_file == NULL) { report("%s has no property input.device", udi); return; } dbg(1, "adding %s", device_file); eventfp = open(device_file, O_RDONLY | O_NONBLOCK); if (!eventfp) { report("Unable to open %s for reading", device_file); return; } if (ioctl(eventfp, EVIOCGID, &id) < 0) { report("failed ioctl EVIOCGID"); close(eventfp); return; } if (id.bustype == BUS_VIRTUAL && id.vendor == 'p' && id.product == 'g' && id.version == 'f') { dbg(1, "declining to monitor our own output"); close(eventfp); return; } if (ioctl(eventfp, EVIOCGBIT(0, EV_MAX), bit) < 0) { report("failed ioctl EVIOCGBIT"); close(eventfp); return; } info = 0; dbg(1, "testing EV_KEY bit"); if (test_bit(EV_KEY, bit) && test_bit(EV_REP, bit)) { info |= IS_KBD; if (is_local_kbd(&id)) info |= IS_LOCAL; report("%s keyboard %s (%02x:%02x:%02x)", (info & IS_LOCAL) ? "matched local" : "found", device_file, id.bustype, id.vendor, id.product); } dbg(1, "testing EV_REL bit"); if ( test_bit(EV_REL, bit) ) { info |= IS_POINTER; if (is_local_tpad(&id)) info |= IS_LOCAL; report("%s pointer %s (%02x:%02x:%02x)", (info & IS_LOCAL) ? "matched local" : "found", device_file, id.bustype, id.vendor, id.product); } if (!(info & IS_KBD) && !(info & IS_POINTER)) { dbg(1, "not kbd or pointer"); close(eventfp); return; } if (ioctl(eventfp, EVIOCGRAB, 1) < 0) { report("add_device: couldn't GRAB %s", device_file); } dbg(1, "Listening on %s", device_file); devices = g_list_prepend (devices, g_strdup (device_file)); channel = g_io_channel_unix_new (eventfp); g_io_channel_set_encoding (channel, NULL, NULL); g_io_channel_set_buffered (channel, 0); g_hash_table_insert (inputs, g_strdup(udi), channel); dbg(1, "adding to watch"); int i = g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, event_io, (gpointer)info, NULL); dbg(1, "watch full returned %d", i); } static void remove_device (LibHalContext *ctx, const char *udi, const LibHalPropertySet *properties) { GIOChannel *channel, **p_channel = &channel; const gchar *device_file; GList *lp; gboolean handling_udi; dbg(1, "remove_device called"); dbg(1, "Removing channel for '%s'", udi); handling_udi = g_hash_table_lookup_extended (inputs, udi, NULL, (gpointer *)p_channel); if (!handling_udi) { report ("DeviceRemove called for unknown device: '%s'.", udi); return; } if (channel) { g_io_channel_shutdown(channel, FALSE, NULL); g_io_channel_unref (channel); } g_hash_table_remove (inputs, udi); if ((device_file = libhal_ps_get_string (properties, "input.device")) == NULL) { report ("%s has no property input.device", udi); return; } lp = g_list_find_custom (devices, device_file, (GCompareFunc) strcmp); if (lp) { devices = g_list_remove_link (devices, lp); g_free (lp->data); g_list_free_1 (lp); } if (g_hash_table_size (inputs) == 0) { report("no more devices, exiting"); g_main_loop_quit (gmain); } } static void init_fifo_glib(int fd) { GIOChannel *channel; unsigned int info; info = IS_CMDFIFO; channel = g_io_channel_unix_new (fd); g_io_channel_set_encoding (channel, NULL, NULL); g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL); g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, event_io, (gpointer)info, NULL); dbg(1, "done with glib fifo init"); } void monitor_input(void) { DBusConnection *dbus_connection; DBusError error; const char *commandline; if (fifo_fd >= 0) init_fifo_glib(fifo_fd); dbus_error_init (&error); if ((ctx = libhal_ctx_init_direct (&error)) == NULL) { report ("Unable to init libhal context"); goto out; } if ((dbus_connection = libhal_ctx_get_dbus_connection(ctx)) == NULL) { report ("Cannot get DBus connection"); goto out; } if ((commandline = getenv ("SINGLETON_COMMAND_LINE")) == NULL) { report ("SINGLETON_COMMAND_LINE not set"); goto out; } dbg(1, "initial ctx calls succeeded"); libhal_ctx_set_singleton_device_added (ctx, add_device); libhal_ctx_set_singleton_device_removed (ctx, remove_device); dbg(1, "set_singleton calls done"); dbus_connection_setup_with_g_main (dbus_connection, NULL); dbus_connection_set_exit_on_disconnect (dbus_connection, 0); dbg(1, "dbus connection setup calls done"); dbus_error_init (&error); if (!libhal_device_singleton_addon_is_ready (ctx, commandline, &error)) { goto out; } dbg(1, "dbus singleton ready call done"); inputs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); gmain = g_main_loop_new (NULL, FALSE); reinit_activity_monitor(); dbg(1, "starting main loop"); g_main_loop_run (gmain); return; out: dbg(1, "An error occured, exiting cleanly"); if (ctx != NULL) { dbus_error_init (&error); libhal_ctx_shutdown (ctx, &error); libhal_ctx_free (ctx); } exit(1); } olpc-kbdshim-27/olpc-kbdshim-udev.c000066400000000000000000000405011174753372400173560ustar00rootroot00000000000000/* * olpc-kbdshim-udev.c: -- special touchpad and keyboard support for XO: * - mouse-based scrolling use the XO grab keys * - touchpad and dpad rotation (to match screen rotation) * - user activity monitoring * * Copyright (C) 2011, Paul G. Fox * * 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, see: http://www.gnu.org/licenses */ #include #include #include #include #include #ifdef PGF_CROSS #include "arm-cross/libudev.h" #else #include #endif #include #include #include "common.h" #define LOG_PROPERTY(path, prop, val) \ dbg(3, "getting property %s on %s " \ "returned \"%s\"", \ (prop), (path), (val) ? (val) : "(null)") #define LOG_SYSATTR(path, attr, val) \ dbg(3, "getting attribute %s on %s " \ "returned \"%s\"", \ (attr), (path), (val) ? (val) : "(null)") static struct udev_monitor *udevmon; int udev_fd = -1; int maxfd = -1; static int fd_flags[FD_SETSIZE]; static char *fd_paths[FD_SETSIZE]; #define IS_KBD 0x01 #define IS_TPAD 0x02 #define IS_CMDFIFO 0x04 #define IS_UDEVMON 0x08 #define IS_OTHER 0x10 #define IS_EBOOK 0x20 #define IS_TSCRN 0x40 #define IS_LOCAL 0x80 #define IS_TPAD_ABS 0x100 /* are we in user-idle state? */ static int idleness; static int wasidle; /* bit array ops */ #define bits2bytes(x) ((((x)-1)/8)+1) #define test_bit(bit, array) ( array[(bit)/8] & (1 << (bit)%8) ) int device_is_duplicate(const char *p, int fd) { int i; dbg(2, "checking syspath %s for dup.", p); for (i = 0; i <= maxfd; i++) { if (!fd_paths[i] || i == fd) continue; dbg(4, "checking against %s.", fd_paths[i]); if (!strcmp(p, fd_paths[i])) return 1; } fd_paths[fd] = strdup(p); return 0; } static void device_added(struct udev_device *udev_device) { const char *path, *name = NULL; const char *syspath; struct udev_device *parent; int dfd; struct input_id id; unsigned char bit[bits2bytes(EV_MAX)]; path = udev_device_get_devnode(udev_device); syspath = udev_device_get_syspath(udev_device); dbg(4, "in device_added, path %s, syspath %s", path, syspath); if (!path || !syspath) return; if ((dfd = open(path, O_RDONLY)) < 0) { report("failed to open input device %s (%s)", name, path); return; } dbg(4, "calling get_property_value"); if (!udev_device_get_property_value(udev_device, "ID_INPUT")) { dbg(4, "ignoring device %s without " "property ID_INPUT set", path); close(dfd); return; } parent = udev_device_get_parent(udev_device); dbg(4, "got parent %p", parent); if (parent) { const char *ppath = udev_device_get_devnode(parent); name = udev_device_get_sysattr_value(parent, "name"); LOG_SYSATTR(ppath, "name", name); if (!name) { name = udev_device_get_property_value(parent, "NAME"); LOG_PROPERTY(ppath, "NAME", name); } } dbg(4, "got name %s, syspath %s", name, syspath); if (!name) name = "(unnamed)"; if (device_is_duplicate(syspath, dfd)) { report("device %s already added. ignoring.", name); close(dfd); return; } if (ioctl(dfd, EVIOCGID, &id) < 0) { dbg(4, "failed ioctl EVIOCGID on %s", path); close(dfd); return; } if (id.bustype == BUS_VIRTUAL && id.vendor == 'p' && id.product == 'g' && id.version == 'f') { dbg(1, "declining to monitor our own output"); close(dfd); return; } dbg(2, "checking input device %s (%s)", name, path); if (ioctl(dfd, EVIOCGBIT(0, EV_MAX), bit) < 0) { report("failed ioctl EVIOCGBIT on %s", path); close(dfd); return; } dbg(4, "done with ioctls on %s, %s", name, path); /* is this the ebook switch? */ if (test_bit(EV_SW, bit)) { unsigned char swbit[bits2bytes(EV_MAX)]; dbg(4, "checking a switch", name, path); if (ioctl(dfd, EVIOCGBIT(EV_SW, SW_CNT), swbit) < 0) { report("failed ioctl EVIOCGBIT EV_SW on %s", path); close(dfd); return; } if (!test_bit(SW_TABLET_MODE, swbit)) { close(dfd); return; } report("fd %d: found ebook switch", dfd); fd_flags[dfd] = IS_EBOOK; if (dfd > maxfd) maxfd = dfd; return; } if (!test_bit(EV_KEY, bit)) { /* heuristic #1 -- all our interesting input devices have * at least one key or button. the accelerometer, in particular, * does not, and we definitely want to skip it. */ dbg(1, "no EV_KEY on %s, ignoring", name); close(dfd); return; } if (test_bit(EV_KEY, bit) && test_bit(EV_REP, bit)) { fd_flags[dfd] = IS_KBD; if (is_local_kbd(&id)) fd_flags[dfd] |= IS_LOCAL; report("fd %d: found keyboard (%s) %s (%02x:%02x:%02x)", dfd, name, path, id.bustype, id.vendor, id.product); } if (test_bit(EV_REL, bit)) { fd_flags[dfd] = IS_TPAD; if (is_local_tpad(&id)) fd_flags[dfd] |= IS_LOCAL; report("fd %d: found touchpad (%s) %s (%02x:%02x:%02x)", dfd, name, path, id.bustype, id.vendor, id.product); /* unless it was already set on the commandline, * establish the path to the disabling node */ if (!local_tpad_enabled[0]) { snprintf(local_tpad_enabled, sizeof(local_tpad_enabled), "%s/device/device/enabled", syspath); } } if (test_bit(EV_ABS, bit)) { unsigned char keybit[bits2bytes(KEY_MAX)]; if (ioctl(dfd, EVIOCGBIT(EV_KEY, KEY_CNT), keybit) < 0) { report("failed ioctl EVIOCGBIT EV_KEY on %s", path); close(dfd); return; } if (test_bit(BTN_LEFT, keybit)) { /* if the absolute device has a left button, we assume that it's * the HGPK pentablet or another similar absolute input mouse. */ fd_flags[dfd] = IS_TPAD_ABS; if (is_local_tpad(&id)) fd_flags[dfd] |= IS_LOCAL; report("fd %d: found absolute touchpad (%s) %s (%02x:%02x:%02x)", dfd, name, path, id.bustype, id.vendor, id.product); /* unless it was already set on the commandline, * establish the path to the disabling node */ if (!local_tpad_enabled[0]) { snprintf(local_tpad_enabled, sizeof(local_tpad_enabled), "%s/device/device/enabled", syspath); } } else { /* It's the touchscreen. we want activity reports from it; we also * grab and filter it to hack in basic swipe and scroll * gesture support. if we have other ABS devices someday * (like some touchpads in native mode), we can check for the * multitouch buttons to reject these. */ struct input_absinfo info; int axes[] = { ABS_X, ABS_Y, ABS_PRESSURE, ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_TOUCH_MAJOR, ABS_MT_WIDTH_MAJOR, ABS_MT_TRACKING_ID, -1 }; int i; fd_flags[dfd] = IS_TSCRN; report("fd %d: found touchscreen (%s) %s (%02x:%02x:%02x)", dfd, name, path, id.bustype, id.vendor, id.product); for (i=0; axes[i] != -1; i++) { if (ioctl(dfd, EVIOCGABS(axes[i]), &info) >= 0) touchscreen_config_axis(axes[i], &info); } setup_uinput(1); } } if ((fd_flags[dfd] & (IS_KBD|IS_TPAD|IS_TPAD_ABS|IS_TSCRN)) == 0) { dbg(1, "%s is not an interesting device", name); close(dfd); return; } if ((fd_flags[dfd] & IS_OTHER) == 0) { if (ioctl(dfd, EVIOCGRAB, 1) < 0) report("couldn't EVIOCGRAB %s", path); else dbg(2, "EVIOCGRAB succeeded %s", path); } if (dfd > maxfd) maxfd = dfd; return; } static void device_removed(struct udev_device *device) { const char *syspath = udev_device_get_syspath(device); dbg(4, "removing syspath %s", syspath); } int setup_input(void) { struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *device; udev = udev_new(); if (!udev) return 0; udevmon = udev_monitor_new_from_netlink(udev, "udev"); if (!udevmon) return 0; udev_monitor_filter_add_match_subsystem_devtype(udevmon, "input", NULL); if (udev_monitor_enable_receiving(udevmon)) { report("failed to bind the udev monitor"); return 0; } enumerate = udev_enumerate_new(udev); if (!enumerate) return 0; udev_enumerate_add_match_subsystem(enumerate, "input"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(device, devices) { const char *syspath = udev_list_entry_get_name(device); struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath); device_added(udev_device); udev_device_unref(udev_device); } udev_enumerate_unref(enumerate); udev_fd = udev_monitor_get_fd(udevmon); return 1; } #if NEEDED void destroy_input(void) { struct udev *udev; if (!udevmon) return; udev = udev_monitor_get_udev(udevmon); udev_monitor_unref(udevmon); udevmon = NULL; udev_unref(udev); } #endif static void device_handler(void) { struct udev_device *udev_device; const char *action; dbg(4, "in device handler"); udev_device = udev_monitor_receive_device(udevmon); if (!udev_device) return; action = udev_device_get_action(udev_device); dbg(4, "further in device handler, action %s", action); if (action) { if (!strcmp(action, "add")) device_added(udev_device); else if (!strcmp(action, "remove")) device_removed(udev_device); } udev_device_unref(udev_device); } static int keyboard_event (int kbd_fd, int is_local) { struct input_event ev[1]; dbg(4, "keyboard_event"); if (read(kbd_fd, ev, sizeof(ev)) != sizeof(ev)) { report("bad read from keyboard, no more fd %d", kbd_fd); close(kbd_fd); fd_flags[kbd_fd] = 0; return 0; } return keyboard_event_worker(ev, is_local); } void touchpad_event(int tpad_fd, int is_local) { struct input_event ev[1]; dbg(4, "touchpad"); if (read(tpad_fd, ev, sizeof(ev)) != sizeof(ev)) { report("bad read from touchpad, no more fd %d", tpad_fd); close(tpad_fd); fd_flags[tpad_fd] = 0; return; } touchpad_event_worker(ev, is_local); } void abs_touchpad_event(int tpad_fd, int is_local) { struct input_event ev[1]; dbg(4, "abs_touchpad"); if (read(tpad_fd, ev, sizeof(ev)) != sizeof(ev)) { report("bad read from touchpad, no more fd %d", tpad_fd); close(tpad_fd); fd_flags[tpad_fd] = 0; return; } abs_touchpad_event_worker(ev, is_local); } void ebook_event(int ebook_fd) { struct input_event ev[1]; dbg(4, "ebook"); if (read(ebook_fd, ev, sizeof(ev)) != sizeof(ev)) { report("bad read from ebook, no more fd %d", ebook_fd); close(ebook_fd); fd_flags[ebook_fd] = 0; return; } ebook_event_worker(ev); } void other_event(int oth_fd, int is_local) { struct input_event ev[1]; dbg(4, "other"); if (read(oth_fd, ev, sizeof(ev)) != sizeof(ev)) { report("bad read from other, no more fd %d", oth_fd); close(oth_fd); fd_flags[oth_fd] = 0; return; } /* do nothing with the data. just consume it. */ } void tscrn_event(int tscrn_fd, int is_local) { struct input_event ev[1]; dbg(4, "tscrn"); if (read(tscrn_fd, ev, sizeof(ev)) != sizeof(ev)) { report("bad read from other, no more fd %d", tscrn_fd); close(tscrn_fd); fd_flags[tscrn_fd] = 0; return; } touchscreen_event_worker(ev, is_local); } static void indicate_idleness(void) { static char useridle[] = "useridleX"; useridle[8] = idleness + '1'; if (idleness < MAXTIMERS) idleness++; send_event(useridle); wasidle = 1; } void reinit_activity_monitor(void) { wasidle = 1; idleness = 0; } static void indicate_activity(void) { if (wasidle) send_event("useractive"); reinit_activity_monitor(); wasidle = 0; } static void get_command(void) { int n; char command[128]; char *cp, *cmdp; n = read(fifo_fd, command, sizeof(command)-1); if (n < 1) { if (n < 0) die("read from rotation fifo"); deinit_fifo(); } command[n] = '\0'; /* more than one command may have been read */ for (cp = command; ; cp = 0) { cmdp = strtok(cp, "\n"); if (!cmdp) break; command_worker(cmdp); } } void monitor_input(void) { fd_set inputs, errors; int r; struct timeval tv; struct timeval *tvp; int pressed; int fd; if (fifo_fd >= 0) { fd_flags[fifo_fd] = IS_CMDFIFO; dbg(4, "fifo_fd is fd %d", fifo_fd); if (fifo_fd > maxfd) maxfd = fifo_fd; } if (udev_fd >= 0) { fd_flags[udev_fd] = IS_UDEVMON; dbg(4, "udev_fd is fd %d", udev_fd); if (udev_fd > maxfd) maxfd = udev_fd; } indicate_activity(); while (1) { FD_ZERO(&inputs); FD_ZERO(&errors); for (fd = 0; fd <= maxfd; fd++) { if (fd_flags[fd]) { FD_SET(fd, &inputs); FD_SET(fd, &errors); } } if (idledeltas[idleness]) { tv.tv_sec = idledeltas[idleness]; tv.tv_usec = 0; tvp = &tv; } else { tvp = 0; } r = select(maxfd+1, &inputs, NULL, &errors, tvp); if (r < 0) die("select failed"); dbg(4, "back from select: %d", r); if (r == 0) { indicate_idleness(); continue; } pressed = 0; for (fd = 0; fd <= maxfd; fd++) { if (!fd_flags[fd]) continue; if (FD_ISSET(fd, &inputs) || FD_ISSET(fd, &errors)) { dbg(4, "fd %d inputs %d errors %d", fd, FD_ISSET(fd, &inputs), FD_ISSET(fd, &errors)); if (fd_flags[fd] & IS_KBD) { pressed |= keyboard_event(fd, !!(fd_flags[fd] & IS_LOCAL)); } else if (fd_flags[fd] & IS_TPAD) { touchpad_event(fd, !!(fd_flags[fd] & IS_LOCAL)); pressed = 1; } else if (fd_flags[fd] & IS_TPAD_ABS) { abs_touchpad_event(fd, !!(fd_flags[fd] & IS_LOCAL)); pressed = 1; } else if (fd_flags[fd] & IS_EBOOK) { ebook_event(fd); } else if (fd_flags[fd] & IS_TSCRN) { tscrn_event(fd, 1); pressed = 1; } else if (fd_flags[fd] & IS_OTHER) { other_event(fd, 0); pressed = 1; } else if (fd_flags[fd] & IS_CMDFIFO) { get_command(); } else if (fd_flags[fd] & IS_UDEVMON) { device_handler(); } } } if (pressed) indicate_activity(); } } olpc-kbdshim-27/olpc-kbdshim-udev.upstart000066400000000000000000000007441174753372400206430ustar00rootroot00000000000000# # olpc-kbdshim implements support for various features # unique to the XO keyboard/touchpad. description "XO laptop keyboard shim" author "Paul Fox" start on stopping rc RUNLEVEL=5 stop on runlevel [016] console output # see "olpc-kbdshim-udev -h" for other options exec /usr/bin/olpc-kbdshim-udev \ -f -l \ -R /var/run/olpc-kbdshim_command \ -A /var/run/powerevents \ -r /usr/bin/olpc-rotate \ -V /usr/bin/olpc-volume \ -b /usr/bin/olpc-brightness respawn olpc-kbdshim-27/olpc-kbdshim.c000066400000000000000000000134261174753372400164230ustar00rootroot00000000000000/* * olpc-kbdshim.c -- special touchpad and keyboard support for XO: * - mouse-based scrolling use the XO grab keys * - touchpad and dpad rotation (to match screen rotation) * - user activity monitoring * * Copyright (C) 2009,2010,2011, Paul G. Fox, inspired in places * by code from mouseemu, by Colin Leroy and others. * * 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, see: http://www.gnu.org/licenses */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" /* are we in user-idle state? */ static int idleness; static int wasidle; /* input event devices */ static int kbd_fd = -1; static int tpad_fd = -1; /* bit array ops */ #define bits2bytes(x) ((((x)-1)/8)+1) #define test_bit(bit, array) ( array[(bit)/8] & (1 << (bit)%8) ) int setup_input() { int i; int dfd; char devname[128]; unsigned char bit[bits2bytes(EV_MAX)]; struct input_id id; for (i = 0; i < EVENTDEVICES; i++) { snprintf(devname, sizeof(devname), "/dev/input/event%d", i); if ((dfd = open(devname, O_RDONLY)) < 0) continue; if (ioctl(dfd, EVIOCGID, &id) < 0) { report("failed ioctl EVIOCGID on %d", i); close(dfd); continue; } if (ioctl(dfd, EVIOCGBIT(0, EV_MAX), bit) < 0) { report("failed ioctl EVIOCGBIT on %d", i); close(dfd); continue; } if (kbd_fd < 0 && test_bit(EV_KEY, bit) && test_bit(EV_REP, bit) && is_local_kbd(&id)) { kbd_fd = dfd; if (ioctl(kbd_fd, EVIOCGRAB, 1) < 0) die("ioctl EVIOCGRAB on keyboard"); report("found keyboard %s (%02x:%02x:%02x)", devname, id.bustype, id.vendor, id.product); continue; } if (tpad_fd < 0 && test_bit(EV_REL, bit) && is_local_tpad(&id)) { tpad_fd = dfd; if (ioctl(tpad_fd, EVIOCGRAB, 1) < 0) die("ioctl EVIOCGRAB on touchpad"); report("found touchpad %s (%02x:%02x:%02x)", devname, id.bustype, id.vendor, id.product); continue; } close(dfd); } if (kbd_fd == -1) report("didn't find keyboard"); if (tpad_fd == -1) report("didn't find touchpad"); return (kbd_fd >= 0 && tpad_fd >= 0); } static int keyboard_event(void) { struct input_event ev[1]; if (read(kbd_fd, ev, sizeof(ev)) != sizeof(ev)) die("bad read from keyboard"); return keyboard_event_worker(ev, 1); } static void touchpad_event(void) { struct input_event ev[1]; if (read(tpad_fd, ev, sizeof(ev)) != sizeof(ev)) die("bad read from touchpad"); touchpad_event_worker(ev, 1); } static void indicate_idleness(void) { static char useridle[] = "useridleX"; useridle[8] = idleness + '1'; if (idleness < MAXTIMERS) idleness++; send_event(useridle); wasidle = 1; } void reinit_activity_monitor(void) { wasidle = 1; idleness = 0; } static void indicate_activity(void) { if (wasidle) send_event("useractive"); reinit_activity_monitor(); wasidle = 0; } static void get_command(void) { int n; char command[128]; n = read(fifo_fd, command, sizeof(command)-1); if (n < 1) { if (n < 0) die("read from rotation fifo"); deinit_fifo(); } command[n] = '\0'; command_worker(command); } void monitor_input(void) { fd_set inputs, errors; int maxfd, r; struct timeval tv; struct timeval *tvp; int pressed; indicate_activity(); maxfd = MAX(fifo_fd, MAX(kbd_fd, tpad_fd)); while (1) { FD_ZERO(&inputs); FD_SET(kbd_fd, &inputs); FD_SET(tpad_fd, &inputs); if (fifo_fd >= 0) FD_SET(fifo_fd, &inputs); FD_ZERO(&errors); FD_SET(kbd_fd, &errors); FD_SET(tpad_fd, &errors); if (fifo_fd >= 0) FD_SET(fifo_fd, &errors); if (idledeltas[idleness]) { tv.tv_sec = idledeltas[idleness]; tv.tv_usec = 0; tvp = &tv; } else { tvp = 0; } r = select(maxfd+1, &inputs, NULL, &errors, tvp); if (r < 0) die("select failed"); if (r == 0) { indicate_idleness(); continue; } pressed = 0; if (fifo_fd >= 0 && FD_ISSET(fifo_fd, &errors)) die("select reports error on rotation event fifo"); if (FD_ISSET(kbd_fd, &errors)) die("select reports error on keyboard"); if (FD_ISSET(tpad_fd, &errors)) die("select reports error on touchpad"); if (fifo_fd >= 0 && FD_ISSET(fifo_fd, &inputs)) get_command(); if (FD_ISSET(kbd_fd, &inputs)) pressed |= keyboard_event(); if (FD_ISSET(tpad_fd, &inputs)) { touchpad_event(); pressed = 1; } if (pressed) indicate_activity(); } } olpc-kbdshim-27/olpc-kbdshim.service000066400000000000000000000005131174753372400176320ustar00rootroot00000000000000[Unit] Description=XO laptop keyboard shim After=syslog.target [Service] # see "olpc-kbdshim -h" for other options ExecStart=/usr/bin/olpc-kbdshim-udev -f -l -R /var/run/olpc-kbdshim_command -A /var/run/powerevents -r /usr/bin/olpc-rotate -V /usr/bin/olpc-volume -b /usr/bin/olpc-brightness [Install] WantedBy=multi-user.target olpc-kbdshim-27/olpc-kbdshim.spec.tmpl000066400000000000000000000143471174753372400201110ustar00rootroot00000000000000Summary: OLPC XO keyboard support daemon Name: olpc-kbdshim Version: __VERSION__ Release: __RELEASE__%{?dist} License: GPLv2+ Group: System Environment/Base URL: http://dev.laptop.org/git/users/pgf/olpc-kbdshim/tree/README # Source0: the source tarball is created by "make tarball" from within # a clone of this git tree: git://dev.laptop.org/users/pgf/olpc-kbdshim Source0: __TARBALL__ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) BuildRequires: kernel-headers, pkgconfig, glib2-devel BuildRequires: dbus-glib-devel, libudev-devel ExclusiveArch: %{ix86} %{arm} Requires: udev, upstart Requires: olpc-utils >= 1.0.26 %description The olpc-kbdshim-udev daemon monitors the keyboard and touchpad, enabling the XO "grab" keys and touchpad rotation (to match screen rotation), and reporting user (in)activity. It can also bind the XO screen rotate, brightness, and volume keys to appropriate commands (which are provided). %prep %setup -q %build export OPT_FLAGS="$RPM_OPT_FLAGS" make %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/%{_bindir} mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/init %{__install} -p -m 755 olpc-kbdshim-udev $RPM_BUILD_ROOT/%{_bindir}/olpc-kbdshim-udev %{__install} -p -m 755 olpc-rotate $RPM_BUILD_ROOT/%{_bindir}/olpc-rotate %{__install} -p -m 755 olpc-brightness $RPM_BUILD_ROOT/%{_bindir}/olpc-brightness %{__install} -p -m 755 olpc-volume $RPM_BUILD_ROOT/%{_bindir}/olpc-volume %{__install} -p -m 644 olpc-kbdshim-udev.upstart $RPM_BUILD_ROOT%{_sysconfdir}/init/olpc-kbdshim-udev.conf %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %doc README COPYING %{_bindir}/olpc-kbdshim-udev %{_bindir}/olpc-rotate %{_bindir}/olpc-brightness %{_bindir}/olpc-volume %config(noreplace) %{_sysconfdir}/init/olpc-kbdshim-udev.conf %changelog * Mon Apr 30 2012 Paul Fox - 27-1 - bug fixes - systemd control files from fedora upstream now bundled * Wed Apr 18 2012 Paul Fox - 26-1 - support absolute reports from pentablet mode (daniel drake) - correct the configuration of the uinput virtual touchscreen (jon nettleton) * Thu Mar 8 2012 Paul Fox - 25-1 - merge of XO-3 touchscreen support, support for 1.75 touchscreens * Fri Feb 3 2012 Paul Fox - 24-1 - use new psmouse driver control to disable touchpad in ebook mode - implement absolute rotation selection in olpc-rotate * Tue Nov 29 2011 Paul Fox - 23-1 - enhancements to olpc-brightness, required by addition of automatic backlight control in powerd. * Mon Nov 14 2011 Paul Fox - 22-1 - audio always unmuted when modified. - changing brightness no longer affects mono/color - use ctrl-brightness-down/up will toggle mono/color * Tue Oct 18 2011 Paul Fox - 21-1 - kbdshim now runs all commands in the fifo, not just the first. - olpc-volume now always unmutes audio * Thu Sep 15 2011 Paul Fox - 20-1 - add support for 1.75 devices (touchscreen and accelerometer) - add ability to suppress keyboard/touchpad in ebook mode * Thu Aug 4 2011 Paul Fox - 19-1 - fix overly-aggressive device scan, which caused us to steal X11's tty1. * Wed Jul 20 2011 Paul Fox - 18-1 - switch over to udev. HAL has been deprecated for some time. - make the RPM available for ARM repos. * Tue Feb 15 2011 Paul Fox - 17-1 - fix bug which caused rotation transoformations to be done on non-motion event types. - major source refactor * Fri Dec 17 2010 Paul Fox - 16-1 - set output device explicitly in olpc-rotate * Mon Oct 11 2010 Paul Fox - 15-1 - fix rotation of d-pad - honor flag file that indicates xrandr goes backwards - ignore repeated rotate keys * Tue Jul 27 2010 Paul Fox - 14-1 - depend on recent olpc-utils, so we have proper f-keys to work with * Thu Jul 15 2010 Paul Fox - 13-1 - bind brightness/volume to KEY_BRIGHTNESSUP/DOWN and KEY_VOLUMEUP/DOWN - add ability to disable these bindings, as well as the older F9-F12 bindings * Fri Mar 12 2010 Paul Fox - 12-1 - reduce periodic logging - bring olpc-kbdshim more in sync with olpc-kbdshim-hal (source changes only) - experimental code for using dbad as a pointing device * Wed Jan 20 2010 Paul Fox - 11-1 - don't pass through command keystrokes when we have a command configured, even if that command fails. * Tue Dec 29 2009 Paul Fox - 10-1 - tell uinput not to autorepeat (thanks to daniel drake, d.l.o ticket #9690) * Tue Dec 1 2009 Paul Fox - 9-1 - add support for binding of local volume keys - added olpc-volume script * Tue Dec 1 2009 Paul Fox - 9-1 - add support for binding of local volume keys - added olpc-volume script * Fri Oct 23 2009 Milos Jakubicek - 7.2 - Fix FTBFS: use %%{ix86} instead of i586 as ExclusiveArch * Thu Jul 30 2009 Paul Fox - 8-1 - add timestamps to events, to reduce racing during suspend/resume * Tue Jul 27 2009 Paul Fox - 7-1 - fix touchpad rotation for F-11, which includes X11 amd/geode driver that fixes the earlier rotation direction issue. (2.11.x and later are fixed.) - revise Makefile for easier pre-release management * Sat Jul 25 2009 Fedora Release Engineering - 6-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Wed Jun 10 2009 Paul Fox - 6-4 - 6-3 - spec tweaks * Sat Jun 6 2009 Paul Fox - 6-2 - final acceptance review changes - olpc-brightness added * Mon May 4 2009 Paul Fox - 6-1 - add local binding of rotate and brightness keys to eliminate sugar patching - revise README * Sat May 2 2009 Paul Fox - 5-1 - we now "grab" the arrow keys so they cause scrolling too - initial fedora review comments incorporated in spec file. * Sun Apr 12 2009 Paul Fox - 4-1 - version numbering resync * Sat Apr 11 2009 Paul Fox - 3-3 - fix sugar patch * Tue Apr 7 2009 Paul Fox - 3-2 - convert to HAL-based operation * Fri Mar 13 2009 Paul Fox - 2-2 - add comments to the post handler, fix rpmlint errors, rename LICENSING to COPYING olpc-kbdshim-27/olpc-kbdshim.upstart000066400000000000000000000007271174753372400177030ustar00rootroot00000000000000# # olpc-kbdshim implements support for various features # unique to the XO keyboard/touchpad. description "XO laptop keyboard shim" author "Paul Fox" start on stopping rc RUNLEVEL=5 stop on runlevel [016] console output # see "olpc-kbdshim -h" for other options exec /sbin/olpc-kbdshim \ -f -l \ -R /var/run/olpc-kbdshim_command \ -A /var/run/powerevents \ -r /usr/bin/olpc-rotate \ -V /usr/bin/olpc-volume \ -b /usr/bin/olpc-brightness respawn olpc-kbdshim-27/olpc-rotate000077500000000000000000000062051174753372400160570ustar00rootroot00000000000000#!/bin/sh # Rotates the XO screen 90 degrees on every invocation # fifo where commands to rotate the touchpad may be written CMD_FIFO=/var/run/olpc-kbdshim_command new_screen="" case $1 in "") # usual case -- rotate to next in counter-clockwise sequence ;; left|right|normal|inverted) # force a specific rotation new_screen=$1 new_tpad=$1 ;; -e) # -e and -n enable/disable axis reflection for the # touchpad. this allows "normal" use of the touchpad # when you're in ebook mode and crack open the laptop # slightly to use the touchpad. otherwise the both # directions are reversed. echo Z >$CMD_FIFO exit 0 ;; -n) echo z >$CMD_FIFO exit 0 ;; -r) # resets touchpad rotation to unrotated, used when X is restarted echo n >$CMD_FIFO exit 0 ;; *) echo "usage: $0 [-e|-n|-r|left|right|normal|inverted]" >&2 exit 1;; esac get_x_credentials() { # fetch the local X server's XAUTHORITY variable eval "$( xargs -n 1 -0 < /proc/$(pidof X)/environ | grep '^XAUTHORITY=')" export XAUTHORITY export DISPLAY=:0 } test "$XAUTHORITY" || get_x_credentials # get current screen orientation if ! xrandrout=$(xrandr --query) then echo xrandr query failed >&2 exit 1 fi # pick up the output device and its current rotation from the same line. # the line looks like: # LCD connected 900x1200+0+0 right (normal left inverted right x axis y axis) 0mm x 0mm # the first word ("LCD") is the output device, "right" is the current # rotation, but in the "normal" case, the word "right" will be missing # entirely. set -- $(echo "$xrandrout" | sed -n 's/^\([a-zA-Z0-9]\+\) *connected .*[0-9] \([a-z]*\) *(.*/\1 \2/p') output="$1" now="$2" if [ ! "$new_screen" ] then # some xrandr/X11-driver implementations will rotate the screen # the wrong way: # - xorg-x11-drv-geode library in F9-based OLPC releases # - SWRandR in F11-based OLPC releases # # in those cases, we allow olpc-utils to set a flag so we can # reverse what we tell xrandr to do. note that in this case # xrandr will be reporting the wrong thing too in $now. if [ -e /var/run/olpc-rotate-reverse ] then # assume xrand reports and performs mirrored rotation. case $now in left) new_screen=normal; new_tpad=normal;; inverted) new_screen=left; new_tpad=right;; right) new_screen=inverted; new_tpad=inverted;; ""|normal) new_screen=right; new_tpad=left;; *) new_screen=normal; new_tpad=normal;; esac else # we always want to cycle the screen counterclockwise case $now in left) new_tpad=inverted;; inverted) new_tpad=right;; right) new_tpad=normal;; ""|normal) new_tpad=left;; *) new_tpad=normal;; esac new_screen="$new_tpad" fi fi # set the new rotation, and inform kbdshim to rotate the touchpad. xrandr --output $output --rotate $new_screen && \ test -e $CMD_FIFO && echo $new_tpad >$CMD_FIFO olpc-kbdshim-27/olpc-volume000077500000000000000000000005701174753372400160670ustar00rootroot00000000000000#!/bin/sh # adjust OLPC XO speaker volume usage() { echo "usage: ${0##*/} {up|down|max|min|<0-100>}" exit 1; } case $1 in max) volchg=100% ;; min) volchg=0 ;; up) volchg="10%+" ;; down) volchg="10%-" ;; 100|[0-9]|[0-9][0-9]) volchg=$1% ;; *) usage ;; esac amixer -q set Master "$volchg" unmute olpc-kbdshim-27/xorg-kbdshim.conf000066400000000000000000000013551174753372400171460ustar00rootroot00000000000000 # when olpc-kbdshim is in use, we want the X server to ignore the # "raw" input devices, since kbdshim will handle them and pass # their (lightly massaged) data through on its virtual output # device. # blacklist mice and keyboards (this includes the XO touchpad): Section "InputClass" Identifier "mouse ignored, handled via kbdshim" MatchIsPointer "on" Option "Ignore" "on" EndSection Section "InputClass" Identifier "keyboard ignored, handled via kbdshim" MatchIsKeyboard "on" Option "Ignore" "on" EndSection # un-blacklist the kbdshim device itself Section "InputClass" Identifier "kbdshim virtual input device" MatchProduct "kbdshim" Option "Ignore" "off" EndSection