Commit Graph

1919 Commits

Author SHA1 Message Date
Jasper St. Pierre
e130a46ab4 Add support for XI2.3: Pointer barrier events and releases.
This adds support for clients that would like to get a notification
every time a barrier is hit, and allows clients to temporarily release
a barrier so that pointers can go through them, without having to
destroy and recreate barriers.

Based on work by Chris Halse Rogers <chris.halse.rogers@canonical.com>

Signed-off-by: Jasper St. Pierre <jstpierre@mecheye.net>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-12-17 15:01:45 +10:00
Peter Hutterer
ce6b652929 Merge branch 'high-resolution-touch-devices' into for-keith 2012-11-29 14:49:22 +10:00
Peter Hutterer
59d70b30e9 dix: use pixman for fp1616 conversions
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Søren Sandmann <ssp@redhat.com>>
2012-11-29 14:48:54 +10:00
Peter Hutterer
2dc6d92284 When resetting device idle time, reset XIAll(Master)Devices too (#56649)
When the screen saver is forcibly deactivated, the idle time counter is
reset for all devices but not for the fake XIAllDevices and
XIAllMasterDevices. XScreenSaverQueryInfo uses XIAlldevices to fill the
"idle" field, thus returning the wrong value.

Regression introduced in
commit 6aef209ebc
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Mon Mar 12 13:51:02 2012 +1000

    Change lastDeviceIdleTime to be per-device

X.Org Bug 56649 <http://bugs.freedesktop.org/show_bug.cgi?id=56649>

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Tested-by: Giacomo Perale <ghepeu@virgilio.it>
Reviewed-by: Keith Packard <keithp@keithp.com>
2012-11-29 14:48:54 +10:00
Jon TURNEY
fb170498ab dix/dispatch.c, os/utils.c: Disable smart scheduler on WIN32
setitimer() and SIGALRM aren't available on WIN32, so smart scheduler
code cannot be built.  Provide only stubs for smart scheduler timer
code, and disable smart scheduler by default.

Signed-off-by: Ryan Pavlik <rpavlik@iastate.edu>
Reviewed-by: Jon TURNEY <jon.turney@dronecode.org.uk>
Tested-by: Yaakov Selkowitz <yselkowitz@users.sourceforge.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
2012-11-28 14:44:12 +00:00
Yuly Novikov
3b9f1c7017 dix: Save touchpoint last coordinates before transform. #49347
DDXTouchPointInfoRec.valuators used to store axis values after transform.
This resulted in Coordinate Transformation Matrix
being applied multiple times to the last coordinates,
in the case when only pressure changes in the last touch event.

Changed DDXTouchPointInfoRec.valuators to store values before transform.

Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=49347

Signed-off-by: Yuly Novikov <ynovikov@chromium.org>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-20 16:06:06 +10:00
Thomas Jaeger
d0fd592fc7 Simplify GetTouchEvents
With only one callee left, we are free to assume that
!(flags & TOUCH_CLIENT_ID)

Signed-off-by: Thomas Jaeger <ThJaeger@gmail.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-20 15:42:46 +10:00
Thomas Jaeger
fe59774c55 Don't use GetTouchEvents in EmitTouchEnd
As before GetTouchEvents causes unwanted side effects.  Add a new
function GetDixTouchEnd, which generates a touch event from the touch
point.  We fill in the event's screen coordinates from the MD's current
sprite position.

Signed-off-by: Thomas Jaeger <ThJaeger@gmail.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-20 15:42:46 +10:00
Thomas Jaeger
cc79107a5b Don't use GetTouchEvents when replaying events
GetTouchEvents has plenty of side effects such as moving the pointer or
updating the master device, which we don't want to happen when
replaying.  The only reason for calling it was to generate a DCCE event,
but GetTouchEvents doesn't even do that right (we might need a DCCE
event even when replaying a master event, or clients could interpret
valuator data incorrectly).

This discussion is moot at the moment anyway, since DeliverTouchEvents
doesn't appear to deliver DCCE events.

Signed-off-by: Thomas Jaeger <ThJaeger@gmail.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>

[Added call to processInputProc instead of direct call to DeliverTouchEvents]

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-20 15:42:01 +10:00
Thomas Jaeger
90b177e5cb Update the MD's position when a touch event is received
Signed-off-by: Thomas Jaeger <ThJaeger@gmail.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-20 15:14:43 +10:00
Thomas Jaeger
aa9da5eae1 remove init_event
The function is identical to init_device_event from inpututils.c with
the first two arguments swapped.

Signed-off-by: Thomas Jaeger <ThJaeger@gmail.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-19 12:12:28 +10:00
Peter Hutterer
fd214aabf7 input: drop FP1616 macro
The double_to_f1616() functions do the same thing, and they're tested.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
2012-11-19 12:12:23 +10:00
Keith Packard
011f845880 Merge remote-tracking branch 'whot/for-keith' 2012-11-05 17:16:07 -08:00
Peter Hutterer
aad65415bf dix: don't allow disabling XTest devices
Disabling a XTest device followed by an XTest API call crashes the server.
This could be fixed elsewhere but disabled devices must not send events
anyway. The use-case for disabled XTest devices is somewhat limited, so
simply disallow disabling the devices.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
2012-11-06 10:48:32 +10:00
Yaakov Selkowitz
e8d45f3018 dix: fix shadow warnings
dispatch.c: In function 'ProcCopyArea':
dispatch.c:1608:5: warning: declaration of 'rc' shadows a previous local
dispatch.c:1604:9: warning: shadowed declaration is here
dispatch.c: In function 'ProcCopyPlane':
dispatch.c:1647:5: warning: declaration of 'rc' shadows a previous local
dispatch.c:1643:9: warning: shadowed declaration is here
events.c: In function 'GetClientsForDelivery':
events.c:2030:68: warning: declaration of 'clients' shadows a global declaration
../include/dix.h:124:28: warning: shadowed declaration is here
events.c: In function 'DeliverEventToWindowMask':
events.c:2113:19: warning: declaration of 'clients' shadows a global declaration
../include/dix.h:124:28: warning: shadowed declaration is here
events.c: In function 'EventSuppressForWindow':
events.c:4420:12: warning: declaration of 'free' shadows a global declaration

Signed-off-by: Yaakov Selkowitz <yselkowitz@users.sourceforge.net>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-05 13:25:00 -06:00
Yaakov Selkowitz
1aa783754e dix: fix redundant redeclaration warnings in dixfont
These functions are already declared in <X11/fonts/fontproto.h>.
Redeclaring them just for _X_EXPORT causes tons of warnings throughout
xserver, but they need to be declared somewhere to be picked up by
sdksyms.sh.  Doing so in a private header limits the warnings to
sdksyms.c; fixing those as well would require changes to fontsproto.

Signed-off-by: Yaakov Selkowitz <yselkowitz@users.sourceforge.net>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-11-05 13:24:59 -06:00
Carlos Garnacho
ced56f322e Sync TouchListener memory allocation with population in TouchSetupListeners()
The allocated TouchListener array may fall short by 1 if hitting the worst case
situation where there's an active grab, passive grabs on each window in the
sprite trace and event selection for touch in one of the windows. This may lead
to memory corruptions as the array is overflown.

Signed-off-by: Carlos Garnacho <carlosg@gnome.org>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-10-30 15:11:09 +10:00
Peter Hutterer
e7cd5cce74 dix: fix zaphod screen scrossing (#54654)
POINTER_SCREEN coordinates are screen-relative. For a Zaphod setup, the
coordinates after a screen crossing are already relative to the new screen's
origin. Add that offset to the coordinates before re-setting.

regression introduced by
commit bafbd99080
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Wed Aug 8 11:34:32 2012 +1000

    dix: work around scaling issues during WarpPointer (#53037)

X.Org Bug 54654 <http://bugs.freedesktop.org/show_bug.cgi?id=54654>

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
2012-10-29 13:15:50 +10:00
Lionel Elie Mamane
c0a752d286 dix: fix Ungrab action #55785
UngrabAllDevices(Bool kill_client):
If we are not going to kill the client (kill_clients false),
we need to deactivate grabs of active clients, too.
(If we are going to kill the client,
 no need to deactivate the grab,
 as this will be done as part of the client kill.)

Fixes: X.Org Bug 55785 <http://bugs.freedesktop.org/show_bug.cgi?id=55785>

Signed-off-by: Lionel Elie Mamane <lionel@mamane.lu>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-10-19 13:12:33 +10:00
Chase Douglas
3b67cd2614 End physically active touches when device is disabled
Otherwise:

* We can't end the touches while device is disabled
* New touches after enabling the device may erroneously be mapped to old
  logical touches

Signed-off-by: Chase Douglas <chase.douglas@canonical.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2012-10-19 13:12:33 +10:00
Peter Hutterer
4b7f00346d dix: fix crash on shutdown if a disabled device is still grabbed (XI1 grab)
A disabled device doesn't have a sprite (less so a sprite->win) and triggers
a NULL-pointer dereference on shutdown when all active grabs are released as
part of the cleanup.

Fix this by checking for sprite being non-null and setting the focus window
to the NullWindow if it is. The rest of the patch just attempts to make
things more readable.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
2012-10-10 14:40:45 +10:00
Keith Packard
8367dd9736 Merge remote-tracking branch 'whot/for-keith' 2012-10-04 13:08:35 -07:00
Peter Hutterer
9d6b836570 dix: fix crash on XI 1.x grabs on disabled devices. (#54934)
If the device is disabled, the sprite window is NULL and dereferencing
crashes the server.

This is only triggered for XI 1.x grabs (ProcXGrabDevice) as XI2 grabs would
trigger another code path, creating a sprite for the disabled device as if
detaching it (which is wrong and fixed with this patch too).

Grabbing a disabled device doesn't make sense as it won't send events
anyway. However, the protocol specs do not prohibit it, so we need to keep
it working.
Luckily, oldWin is only used for focus out events, which aren't necessary
given that the device is disabled.

X.Org Bug 54934 <http://bugs.freedesktop.org/show_bug.cgi?id=54934>

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Chase Douglas <chase.douglas@ubuntu.com>
2012-10-04 13:24:43 +10:00
Keith Packard
4dd5989d15 Merge remote-tracking branch 'ajax/server-1.14-abi-churn' 2012-09-24 11:43:01 -07:00
Yufeng Shen
0b02150c27 dix: fix scale_to_desktop for edge ABS events
Scale_to_desktop() converts ABS events from device coordinates
to screen coordinates:
[dev_X_min, dev_X_max]  -> [screen_X_min, screen_X_max]
[dev_Y_min, dev_Y_max]  -> [screen_Y_min, screen_Y_max]

An edge ABS event with X = dev_X_max (e.g., generated from the
edge of a touchscreen) will be converted to have screen X value
= screen_X_max, which, however, will be filterd out when xserver
tries to find proper Window to receive the event, because the
range check for a Window to receive events is
       window_X_min <= event_screen_X < window_X_max
Events with event_screen_X = screen_X_max will fail the test get
and rejected by the Window.

To fix this, we change the device to screen coordinates mapping to
[dev_X_min, dev_X_max]  -> [screen_X_min, screen_X_max-1]
[dev_Y_min, dev_Y_max]  -> [screen_Y_min, screen_Y_max-1]

Reviewed-by: Chase Douglas <chase.douglas@canonical.com>
Reviewed-by: Jeremy Huddleston Sequoia <jeremyhu@apple.com>
Signed-off-by: Yufeng Shen <miletus@chromium.org>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-09-24 11:12:04 -07:00
Adam Jackson
ad0156c369 dix: Remove MapUnmapEventsEnabled and friends
This hack was added to suppress events generated by Composite's internal
unmap/map cycle on redirection state change.  Since that cycle was
removed in 193ecc8b4, these can go.

Signed-off-by: Adam Jackson <ajax@redhat.com>
Reviewed-by: Ville Syrjälä <syrjala@sci.fi>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-09-23 10:41:54 -07:00
Adam Jackson
387b1ac33c dix: Factor out DeliverUnmapNotify
Signed-off-by: Adam Jackson <ajax@redhat.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-09-23 10:41:41 -07:00
Adam Jackson
d20cc0fca4 dix: Factor out DeliverMapNotify
Signed-off-by: Adam Jackson <ajax@redhat.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-09-23 10:41:26 -07:00
Adam Jackson
63843cb700 dix: Factor out MaybeDeliverMapRequest
Signed-off-by: Adam Jackson <ajax@redhat.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-09-23 10:40:38 -07:00
Adam Jackson
dab7a1ec7f dix: Fix some indentation
Signed-off-by: Adam Jackson <ajax@redhat.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-09-23 10:40:08 -07:00
Peter Hutterer
3d1051aecb dix: set the device transformation matrix
The property handler is registered after setting the property, so
dev->transform remains as all-zeros. That causes pixman_f_transform_invert()
to fail (in transformAbsolute()) and invert remains as garbage. This
may then cause a cursor jump to 0,0.

Since the axes are not yet initialized here and we need to allow for drivers
changing the matrix, we cannot use the property handler for matrix
initialization, essentially duplicating the code.

Triggered by the fix to (#49347) in 749a593e49

https://bugzilla.redhat.com/show_bug.cgi?id=852841

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Chase Douglas <chase.douglas@ubuntu.com>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-09-23 10:38:42 -07:00
Adam Jackson
e2c7d70e5d dix: Extend initial connection handshake for forwarding proxies
Forwarding proxies like sshd will appear to be local, even though they
aren't really.  This leads to weird behaviour for extensions that truly
require running under the same OS services as the client, like MIT-SHM
and DRI2.

Add two new legal values for the initial connection's byteOrder field,
'r' and 'R'.  These act like 'l' and 'B' respectively, but have the side
effect of forcing the client to be treated as non-local.  Forwarding
proxies should attempt to munge the first packet of the connection
accordingly; older servers will reject connections thusly munged, so the
proxy should fall back to passthrough if the munged connection attempt
fails.

Reviewed-by: Daniel Stone <daniel@fooishbar.org>
Signed-off-by: Adam Jackson <ajax@redhat.com>
2012-09-20 14:40:18 -04:00
Dave Airlie
49ec57d509 dix: free default colormap before screen deletion
If we don't free this here, it gets freed later in the resource
cleanups, however it then looks up up pmap->pScreen, which we
freed already in this function. So free the default colormap
when we should.

This fixes a bug after a couple of hotplug cycles when you try
to exit the X server and it crashes.

Reviewed-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
2012-09-04 16:15:52 +10:00
Keith Packard
a557edca61 Merge remote-tracking branch 'whot/for-keith' 2012-08-27 08:06:09 -07:00
Peter Hutterer
bafbd99080 dix: work around scaling issues during WarpPointer (#53037)
In WarpPointer calls, we get input in screen coordinates. They must be
scaled to device coordinates, and then back to screen coordinates for screen
crossing and root coordinates in events.

The rounding errors introduced (and clipping in core/XI 1.x events) can lead
to the actual position being different to the requested input coordinates.
e.g. 200 scales to 199.9999, truncated to 199 in the event.

Avoid this by simply overwriting the scaled screen coordinates with the
input coordinates for the POINTER_SCREEN case.

X.Org Bug 53037 <http://bugs.freedesktop.org/show_bug.cgi?id=53037>

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
2012-08-21 07:54:07 +10:00
Keith Packard
288b87e42c Close GPU screens before core screens
This should make cleaning up the GPU screens easier as the core
screens they are associated with will still be around.

Signed-off-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Dave Airlie <airlied@redhat.com>
2012-08-14 17:13:52 -07:00
Keith Packard
02f94b2d44 Merge remote-tracking branch 'whot/for-keith' 2012-08-06 16:52:12 -07:00
Keith Packard
360fa7736b Merge remote-tracking branch 'airlied/for-keithp' 2012-08-06 16:42:34 -07:00
Peter Hutterer
cb306a8f17 dix: make sure the mask is set for emulated scroll events (#52508)
If a device has smooth scrolling axes, but submits scroll button events, we
convert those to motion events and update the valuators. For legacy button
events, the valuator mask is likely unset though, causing
add_to_scroll_valuator() to return early, leaving us with an empty mask.
That again skipped the rest of the code and no events were generated.

Fix it by making sure that the scroll valuator in the mask is at least
initialized to 0.

Broke evdev wheel emulation, introduced by
54476b5e44.

X.Org Bug 52508 <http://bugs.freedesktop.org/show_bug.cgi?id=52508>

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Chase Douglas <chase.douglas@canonical.com>
2012-08-07 09:39:56 +10:00
Dave Airlie
1a465fef9b pixmap: have slave pixmap take a reference on master pixmap
Since the free routines free the master pixmap then the slave, we should
be taking a reference when we bind them together.

Fixes a use-after-free when resizing a primed gears.

Signed-off-by: Dave Airlie <airlied@redhat.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
2012-08-07 08:25:06 +10:00
Alan Coopersmith
c37c65052f Make indentation of dix/tables.c much more consistent and readable
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Acked-by: Daniel Stone <daniel@fooishbar.org>
2012-08-06 15:22:53 -07:00
Alan Coopersmith
9f7ef7f7f0 Fix up formatting of initializers for arrays of structs
The indenter seems to have gotten confused by initializing arrays of
structs with the struct defined inline - for predefined structs it did
a better job, so match that.

Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
2012-08-06 15:22:53 -07:00
Daniel Stone
59c2c4f645 AllocDevicePair: Ensure XKB privates are initialised
Since we call directly into XKB and may be doing so before the extension
has been initialised, make sure its privates are set up first.  XTest
had a hack to do this itself, but seems cleaner to just make sure we do
it in AllocDevicePair.

Signed-off-by: Daniel Stone <daniel@fooishbar.org>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Keith Packard <keithp@keithp.com>
2012-07-10 23:12:39 -07:00
Keith Packard
34cf559bcf ProcGetPointerMapping uses rep.nElts before it is initialized
In:

	commit d792ac125a
	Author: Alan Coopersmith <alan.coopersmith@oracle.com>
	Date:   Mon Jul 9 19:12:43 2012 -0700

	    Use C99 designated initializers in dix Replies

the initializer for the .length element of the xGetPointerMappingReply
structure uses the value of rep.nElts, but that won't be set until
after this initializer runs, so we get garbage in the length element
and clients using it will generally wedge.

Easy to verify:

	$ xmodmap -pp

Fixed by creating a local nElts variable and using that.

Signed-off-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Alan Coopersmith <alan.coopersmith@oracle.com>
2012-07-10 23:07:06 -07:00
Keith Packard
6e12cb147d Merge branch 'local-fixes' 2012-07-10 00:52:11 -07:00
Alan Coopersmith
ad4092cf7d Replace padlength tables with inline functions from misc.h
Adds new function padding_for_int32() and uses existing pad_to_int32()
depending on required results.

Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Tested-by: Daniel Stone <daniel@fooishbar.org>
2012-07-09 22:52:30 -07:00
Alan Coopersmith
1622dd8ab2 Use C99 designated initializers in dix registry
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Tested-by: Daniel Stone <daniel@fooishbar.org>
2012-07-09 22:52:30 -07:00
Alan Coopersmith
0af79b124e Use C99 designated initializers in dix Events
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Tested-by: Daniel Stone <daniel@fooishbar.org>
2012-07-09 22:52:30 -07:00
Alan Coopersmith
d792ac125a Use C99 designated initializers in dix Replies
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Tested-by: Daniel Stone <daniel@fooishbar.org>
2012-07-09 19:58:29 -07:00
Alan Coopersmith
69fa5630b5 Use C99 designated initializers in SendErrorToClient
Let the compiler worry about 0-filling the rest of the fields,
instead of memsetting the whole struct and then going back to
overwrite some of the fields.

Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Tested-by: Daniel Stone <daniel@fooishbar.org>
2012-07-09 19:58:29 -07:00