diff --git a/gnome-control-center-2.23.4.tar.bz2 b/gnome-control-center-2.23.4.tar.bz2 deleted file mode 100644 index 6ebd172..0000000 --- a/gnome-control-center-2.23.4.tar.bz2 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f85feb6064262bc9e232ae051beec49d1f72f6c6788837e17ea1726e803692a -size 2176661 diff --git a/gnome-control-center-2.23.6.tar.bz2 b/gnome-control-center-2.23.6.tar.bz2 new file mode 100644 index 0000000..d93357e --- /dev/null +++ b/gnome-control-center-2.23.6.tar.bz2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc561d9cbf0390a729c0140c17802661754a76bde346d582406d562cf7026828 +size 2137024 diff --git a/gnome-control-center-randr-1.2.diff b/gnome-control-center-randr-1.2.diff deleted file mode 100644 index 173d747..0000000 --- a/gnome-control-center-randr-1.2.diff +++ /dev/null @@ -1,5337 +0,0 @@ -diff --git a/capplets/display/Makefile.am b/capplets/display/Makefile.am -index b75da5b..193fd65 100644 ---- a/capplets/display/Makefile.am -+++ b/capplets/display/Makefile.am -@@ -1,9 +1,18 @@ - # This is used in GNOMECC_CAPPLETS_CFLAGS - cappletname = display - -+gladedir = $(pkgdatadir)/glade -+dist_glade_DATA = display-capplet.glade -+ - bin_PROGRAMS = gnome-display-properties - --gnome_display_properties_SOURCES = main.c -+gnome_display_properties_SOURCES = \ -+ xrandr-capplet.c \ -+ scrollarea.c \ -+ foo-marshal.c \ -+ scrollarea.h \ -+ foo-marshal.h -+ - gnome_display_properties_LDFLAGS = -export-dynamic - gnome_display_properties_LDADD = \ - $(DISPLAY_CAPPLET_LIBS) \ -diff --git a/capplets/display/TODO b/capplets/display/TODO -new file mode 100644 -index 0000000..a1164e1 ---- /dev/null -+++ b/capplets/display/TODO -@@ -0,0 +1,815 @@ -+Highlevel overview: -+ -+- GTK+ work. -+ -+ Allow applications to be notified whenever monitors are added -+ or removed. Allow applications to get more detailed -+ information about the connected monitors. -+ -+ The main complication is that XRRGetScreenResources() is very -+ slow. We could call it only when the X server sends an event, -+ but it's not desirable to have every application freeze for -+ half a second. And certainly not desirable to have the X -+ server block for n * 0.5 seconds. -+ -+ With the X server work below we should be fine just calling -+ XRRGetScreenResources on startup and in response to events. -+ -+- X server work: -+ -+ X server needs to poll for whether a monitor is plugged -+ in. Whenever it detects a change, it should do an EDID query, -+ and cache the resulting information. That way XRRGetScreenResources() -+ can be the speed of a normal roundtrip. It's desirable that -+ normal client requests can still be processed during the EDID -+ querying, but only a nice-to-have. -+ -+ Drivers need to work reliably. There could be substantial work -+ here. For F9, possibly only the Intel driver can be made to -+ work. -+ -+ Interrupts and events must be generated whenever something changes -+ about the outputs, if necessary by polling. -+ -+ Events must be emitted whenever something changes, including when -+ the reason for the change is a manual change. -+ -+ The maximum framebuffer must be dynamically changable. -+ -+- Control panel work: -+ Capplet needs to be written. The main complications: -+ -+ - It needs to pay attention to events from the X server -+ and update itself, ie., add show new monitors if they become -+ available when the applet is shown. -+ -+ - It needs to store information under a key computed -+ from a monitor identifier. The complication here is that -+ it's not completely clear how to do this in GConf. -+ -+ - Would probably be worthwhile to drop libgnome/libgnomeui from -+ the craplets. -+ -+- Metacity work: -+ - Metacity is already Xinerama aware, but it needs to update itself -+ when monitors come and go. -+ -+- GNOME panel work: -+ - Is already Xinerama aware, but needs to listen and update itself -+ when monitors change. -+ -+- Evince work: -+ - Make sure it deals sensibly with multiple monitors -+ -+- OpenOffice work: -+ - Make sure it deals sensibly with multiple monitors -+ -+- An Xlib call to just return all the available information would be -+ useful. At the moment we have to do a bunch of roundtrips to -+ get the information. This is a would-be-nice though. -+ -+- A dbus service could be written that pops up the applet whenever a -+ monitor. It should only pop up if the new monitor is unknown. This -+ is at best a nice-to-have, and low priority in my opinion. -+ -+ -+******************* Metacity -+ -+Havoc: -+ -+> I was just talking to bryan about this and "helping" him design it ;-) -+ -+> But I wanted to be sure and lobby for a fix window managers -+> need. Basically right now the WM can't tell "physical" from -+> "logical" monitors. -+ -+> A "logical" monitor is a desktop; it has its own panel, windows -+> maximize to it, etc. -+ -+> A "physical" monitor is a piece of hardware. -+ -+> Sometimes people want to combine physical monitors into a video wall -+> or just two monitors treated as one. Or at least a couple of noisy -+> people in bugzilla want to do this. -+ -+> When people talk about a "Xinerama aware" app or WM they usually -+> mean that all physical monitors are treated as logical monitors, -+> while lack of Xinerama-aware means treating the entire X screen (all -+> physical monitors) as one logical monitor. -+ -+> The problem is that the setting for "ignore Xinerama" or "don't be -+> Xinerama aware" should be global to the desktop (GTK, all apps, WM) -+> and should not be a window manager setting. -+ -+> Bryan thought people who wanted non-Xinerama-aware should just use -+> fvwm, which may be right, but what I'd say is that if there is any -+> setting for this, it should be desktop-global and in this monitor -+> config dialog. -+ -+> It should not be a metacity or Compiz option, but in some way an X -+> option in short. The implementation could be either an X server -+> feature or an EWMH hint or whatever, but it should be controlled by -+> the monitor config dialog and used by apps, GTK, etc. in addition to -+> used by the WM. -+ -+> People tend to insist this should be a WM option, but that's just -+> busted, since GTK and apps also have Xinerama-awareness features. -+ -+ -+******************* EDID -+ -+edid-decode enhancements: -+ -+- Rejects years <= 0x0f for all versions, but this should only be done -+ for monitors claiming conformance to 1.4 (since 1.4 was released in -+ 2006). A monitor produced in 2005 should have 0x0f - it's the only -+ reasonable thing to do. -+ -+- Uses 0x80 as the conformance mask for 1.4, should be 0 -+ -+- Should read from stdin -+ -+- Should parse xrandr -verbose output more robustly -+ -+- Color depth computation is wrong. It uses the formula -+ -+ (edid[0x14] >> 3) + 2 -+ -+ The correct formula to use is -+ -+ (edid[0x14] & 0x70) >> 3 + 4 -+ -+- -+ -+-=-=-=- -+Computing a display name from EDID information: -+ -+ vendor = lookup_vendor (code); -+ -+ if (dsc_product && !is_gobbledigook (dsc_product)) -+ { -+ if (vendor && !fuzzy_string_search (vendor, dsc_product)) -+ prepend (vendor); -+ } -+ else -+ { -+ if (vendor) -+ append (vendor); -+ else -+ append ("Unknown"); -+ } -+ -+ if (has size) -+ { -+ convert_to_inches() -+ -+ append (" %d\"", inches) -+ } -+ -+(Does this internationalize at all)? -+ -+We also need the ability to get laptop names. The laptop panel may report -+a manufacturer that has nothing to do with the laptop manufacturer. -+ -+Needed XRandr output properties: -+ -+- Modes that the monitor supports, or enough information that the -+ client can go throught the list of modes for the relevant -+ CRTC/Outputs and filter those out that the monitor can't support. -+ -+- The preferred mode, if any. Also useful if we could get a "strongly -+ preferred" indication if it's an LCD with a fixed resolution. -+ -+- Sufficient information that a fairly specific identifier can be -+ computed. The algorithm the client should use is: -+ -+ 1 Have we seen exactly this monitor before? If yes, use -+ settings for that. -+ -+ 2 Have we seen a monitor with similar specs before? If yes, -+ use settings for that. (But don't save, unless the user -+ changes the settings). -+ -+ 3 Otherwise, use some reasonable default for the monitor and -+ save it. -+ -+ A setting should only be used if the CRTC/Output allows it. Ie,. if -+ a user has installed a new video card, then previously-used settings -+ may no longer apply, so this must be checked every time. -+ -+ (1) Implies that we really need a globally unique identifier for -+ monitors. (2) is useful in an enterprise setting, but not absolutely -+ critical, since (3) would still handle the majority of cases. -+ -+ There is a question here: Where are machine specific preferences -+ stored? Havoc mentions three possibilities here: -+ -+ http://mail.gnome.org/archives/gnomecc-list/2001-October/msg00023.html -+ -+ I'm not sure if any of them are implementable at this point. Also -+ (1) may mostly take care of the problem. -+ -+ -+ Usecases: -+ -+ 1. Fixed setup with some number of monitors. -+ - They should be set to the correct mode on login. -+ Note that this involves setting the right position in the -+ framebuffer too. -+ What if someone swaps two monitors? Users are going to expect -+ that the images will switch position. -+ -+ 2. Laptop being moved between home and work -+ - Setups should be detected and the correct mode set, at least on -+ login, but ideally when you put the laptop into the docking -+ station. -+ -+ 3. Laptop gets projector plugged in. -+ -+ Note the same model monitor can be used in two different ways. Ie., -+ at home, it's being used at one resolution, at work the same type of -+ monitor is used at a different resolution. -+ -+ Simple solution: -+ -+ - The on-disk database is just a list of monitors. Each monitor has an -+ associated mode. This has these problems: -+ - If someone uses the same monitor model in two different ways. -+ - If someone swaps the monitors around -+ -+ Better solution -+ -+ - The on-disk database is a list of configurations, where a -+ configuration is a list of monitors and what outputs they are -+ connected to, and the position in the framebuffer. -+ -+ - Picking a default configuration is then a matter of selecting the -+ closest existing configuration from the database. -+ -+ - If the stored configuration is a subset of the existing, -+ then use that - then pick the best mode available for the -+ rest of the monitors -+ -+ - If the stored configuration is a superset of the existing, -+ then use the projection of the configuration onto the monitors. -+ -+ - Pick the configuration with the most overlap in monitors. -+ Although, if a configuration differs only in what outputs -+ they are connected to, then those outputs should probably -+ get their original modes set. -+ -+ - Or maybe simply: -+ -+ - If there is an exact match, use it, if not, pick a default. -+ -+ - Picking a new default must never change the mode of any existing -+ output. -+ -+******************* Capplet -+ -+Somehow the applet will find out that a new monitor is plugged in -+(either through notification, or through a refresh button). When this -+happens, this monitor is looked up in a database and if it is found, -+some suitable mode is set. -+ -+Restrictions on the modes: -+ -+- Monitors that are already plugged in should not get their mode -+ changed just because a new monitor is plugged in. -+ -+- If the exact configuration of monitors is known, and all the old -+ monitors have the same mode as the known configuration, then just use -+ the known configuration. Also do this, if the configuration is a -+ subset of something known. -+ -+- Otherwise, if the configuration is a subset of a known configuration -+ where the only difference is that existing monitors have different -+ modes, then try and convert that mode to something we can know -+ about. Maybe configurations should be stored in terms of edges that -+ line up. -+ -+- Otherwise, just pick some good default for the mode, probably based -+ on the EDID prferred mode if possible. By default cloning is -+ probably best. -+ -+- How do virtual desktops interact with this? -+ -+ -+g-s-d: -+ -+- On startup -+ -+ - It reads the configuration file into memory -+ -+ capplet --configure -+ -+ - It gathers the existing configuration from randr -+ -+ - If the existing config is in the file, set that mode -+ -+- On changes, including changes to the config file [this is crack] -+ -+ - Reread configuration file -+ -+ - Compare new configuration to database, if it is there, set the -+ mode as appropriate -+ -+ - If a monitor was added, pop up a bubble -+ -+ capplet --show-bubble -+ -+ capplet --set-mode -+ -+capplet -+ -+- On changes -+ -+ - Update GUI -+ -+- When user changes something, -+ -+ - Write configuration to file -+ -+ - Signal gsd somehow -+ -+Schemes: -+ - configuration file changes -+ - randr code will have to be shared between gcc and gsd -+ -+ - binary installed by gcc -+ - something will still have to listen for changes to pop -+ up the notification bubble. -+ -+Structure of capplet: -+ -+- There is a database on disk with monitors and their corresponding -+ settings. -+ -+- On startup, this database is read into memory. When the user accepts -+ new settings, it is written back to disk. -+ -+- When something changes about the settings -+ -+ - If new configuration is in the database, use that mode -+ -+ - Else, find all outputs that are now connected but weren't before, -+ and set a default mode for them. -+ -+ - If GUI is running, update graphics. -+ -+ -+ - Notification thing: -+ - if -+ -+ - if the new configuration is found in the database, use it -+ -+ and added if they are not already there. Initial settings are -+ 1 what the output is already doing, if anything -+ 2 based on an existing sufficiently similar monitor, if possible -+ 3 some reasonable default. -+ -+- When the user changes settings in the GUI, the corresponding monitor -+ in the database is updated. -+ -+- Whenever the GUI settings change, for all displayed monitors the -+ possible modes are recomputed. -+ -+- Whenever a new monitor is selected in the GUI, it first gets all its -+ possible modes computed based on the selections on other -+ outputs. Then, if the possible modes include the existing choice of -+ resolution, that is selected. -+ -+ Actually, -+ -+ - initially, the settings are copied from the current settings -+ -+ - whenever a gui setting changes for a monitor, all the other -+ monitors get their list of choices set to whatever is possible -+ given the chocie for the current monitor. A 'desired mode' is -+ maintained, and the closest choice to that is displayed. Whenever -+ the user actively selects something, that becomes the desired mode -+ for that monitor. -+ -+- Required -+ -+ - Generate all outputs that are newly connected -+ -+ foreach_newly_connected (Configuration *before, Configuration *after, -+ OutputFunc); -+ -+ - A way to generate the best mode for a connected output -+ -+ existing best_mode() can probably be used -+ -+ - Given a list of modes, pick the one closest to a given mode. -+ -+ (a possibility here is: pick an exact match, if that's impossible, then -+ pick the best one with the same width/height, if that's impossible, then -+ just pick the best mode on the list). -+ -+ - For a configuation, fix the mode for a subset of the outputs, then list the -+ combinations for the rest of the outputs. -+ -+ An obvious possibility here is to simply list all possibilities, then -+ weed out those that don't work. Is this too expensive? It might be. -+ -+Structure of login time program: -+ -+- The configuration database is read -+ -+- The current hardware configuration is generated -+ -+- If the current configuration is found in the database, that mode is set. -+ -+- If it isn't found, then nothing changes. -+ -+ This could just be gnome-screen-resolution-capplet --reset -+ -+******************* Things that need to be done to the xrandr.patch: -+ -+=== -+ -+XRRGetScreenResources() is a roundtrip and very slow (~0.5 s). GTK+ -+needs to keep information up-to-date by tracking events rather than -+calling this function. In fact we probably can't call it at all unless -+its performance improves significantly. -+ -+If EDID processing really has to be this slow, and we can't get -+interrupts when monitors are plugged in, then we have a problem, -+because we can't do anything this expensive once per second. -+ -+ -+Detailed notes (but most of the patch should be rewritten): -+ -+ -+=== FIXME in gdkscreen-x11.c in get_width_mm() -+ -+/* monitor pixel width / screen pixel width * screen_physical width */ -+ -+ -+ -+=== Check for 1.2 library -+ -+The patch should check that the 1.2 version of the XRandR library is -+available before using the functions. A possibility is to not use any -+RandR unless 1.2 is available, another is to conditionalize the code. -+ -+The most sane thing is probably to just require 1.2. -+ -+On the other hand, installing a newer gtk+ on a system with older X is -+probably not that unusual, so maybe it's better to do the full 1.0, -+vs. 1.1 vs 1.2 check. -+ -+For now it just requires 1.2. -+ -+Actually, this might be fine because the only place where we make use -+of a 1.1 library is in the _gdk_x11_screen_size_changed() function, -+but there we have a fallback that just updates the variables in the -+Screen struct itself. -+ -+So, only defining HAVE_RANDR if we detect 1.2 should be ok. -+ -+=== Monitor information available -+ -+- Subpixel information. This should be set automatically for the fonts and -+ store under the name of the monitor. If the user changes the font -+ configuration, that change should also be stored under the monitor name. -+ -+- When a monitor we don't know about is plugged in, a configuration should -+ be generated: -+ -+ - Screen size, computed based on the location of the screens -+ -+ - RGBA information -+ -+ - Whether the screen has a panel on it -+ -+ - If there is a conflict between stored information and EDID, -+ the stored information wins -+ -+ -+ -+New API so far: -+ -+(* monitors_changed) signal -+gdk_screen_get_monitor_width_mm() -+gdk_screen_get_monitor_height_mm() -+gdk_screen_get_monitor_name() => Note this is the output (eg. "DVI-0") -+ -+We should probably also have -+get_manufacturer() -+get_serial() -+get_resolutions() -+ -+etc. -+ -+Should there be a GdkMonitor object that would correspond to an -+output? Or maybe GdkOutput? -+ -+screen_list_monitors() -+ -+ -+*************************** Issues XRandR/Xserver -+ -+- We need polling in the X server, whenever something changes, X must -+ recompute the information and cache it, then send an event. Note the -+ situation where the user disconnects and reconnects a monitor within -+ the polling interval. The event could missed in that case since the polling -+ cannot do a full EDID query. Difficult to see a way around this. -+ -+ Actually, DDC allows random access, so it should be possible to just -+ read theq vendor id and manufacturer codes. This can be done once a -+ second without a problem. The polling should be turned off in power -+ saving mode anyway. -+ -+ - Driver work: -+ -+ - Intel driver: -+ -+ - EDID information is not reported for VGA when the output is not -+ turned on (i945 laptop). -+ -+ - Screen size must be dynamically changable. (No xorg.conf changes -+ should be required). -+ -+ - Make use of ACPI information when possible. -+ -+ Adam has code on his freedesktop page. -+ -+ - i830 laptop can be put in a state where XRandr reports that no -+ outputs are connected to a CRTC, but the panel is on. -+ -+ - Plug in VGA -+ - xrandr --auto -+ - xrandr --output VGA --off -+ - run chk -+ - xrandr --verbose will now not report any outputs as turned on -+ - run chk again - all screens will be turned off -+ -+ - Small Sun monitor - an 1152x921 mode is generated, but the -+ monitor doesn't handle that. The monitor itself only claims to -+ handle 1152x920. It doesn't look to me like there is anything -+ in the EDID information that would indicate that it could handle -+ 1152x921. -+ -+ This happens with a radeon as wellso it may be a bug in the -+ generic X server EDID parsing. The X server apparently -+ interpretes the standard timing 1152x920 as 1152x921. -+ -+ This happens because the X server uses -+ -+ hsize * 4 / 5 -+ -+ which gives 921 for 1152. By using -+ -+ (hsize / 5) * 4 -+ -+ you get 920. The 66 Hz version can bet set, the 76 Hz mode gets -+ sync out of range. (Would be interesting to find out whether the -+ 1152x920 ModeLine would allow the 76 Hz version to be set). -+ -+ This is for the ATI driver as shipped in F8: -+ -+ - XRRGetScreenResources() takes half a second. -+ -+ - Adam has now removed a workaround that caused some of the slowdown. -+ -+ - If a DVI monitor is disconnected, you get "Unknown" for connection -+ status. -+ -+ - If a VGA monitor is plugged in, then EDID information is not -+ available, even after running xrandr --verbose. The monitor has -+ to be plugged in at driver startup time, apparently. -+ -+ - Logging out and logging back in often results in some random mode being -+ set. We need mode selection to not be completely screwed up. -+ Currently it is. -+ -+ - The set up at server startup needs to be fixed. *If* randr actually works, -+ then we might be able to do something sensible. -+ -+ - We need to revisit the idea that many monitors have broken EDID data. -+ This may be less widespread than previously believed. -+ -+- It may be useful to return the connector names as identifiers instead -+ of relying on UTF-8 strings. Ie., have an enum -+ -+ { UNKNOWN, OTHER, DVI, VGA, HDMI, ..., } -+ -+ in addition to the string. The difference between UNKNOWN and OTHER is that -+ UNKNOWN means the driver doesn't know, whereas OTHER means it is something -+ not listed in the enum (which could be listed in a later version). -+ -+- Mouse cursor should be confined to the visible area. (It is already, I think) -+ -+- It looks like EDID information is only available for one output -+ even though it is actually read according to the log file. -+ (nv, intel drivers) -+ -+ -+********************************* -+ -+ -+ -+DONE: -+ -+Server work: -+ -+ - i830 laptop incorrectly reports BadMatch when you configure the -+ CRTC to drive both VGA and LVDS with the 1024x768 mode that both -+ outputs can handle. (It should return 'failed' if it can't do -+ that). Same for i945 laptop. It seems as if the same CRTC can't -+ drive more than one output at the same time on Intel. -+ -+ This was a client bug, but the documentation for SetCrtcConfig -+ should say that BadMatch will be returned if the outputs aren't -+ clones. -+ -+GTK+ patch is in now. -+ -+=== Add helper function -+ -+ -++ if (screen_x11->randr12) -++ { -++ XRRScreenResources *sr; -++ XRROutputInfo *output; -++ gchar *retval; -++ -++ sr = XRRGetScreenResources ( screen_x11->xdisplay, -++ screen_x11->xroot_window ); -++ -++ output = XRRGetOutputInfo ( screen_x11->xdisplay, -++ sr, -++ (RROutput)screen_x11->act_outputs[monitor_num] -+); -+ -+ Might be worthwhile to factor this out into a -+ gdk_screen_get_output_info (screen, monitor_num) -+ helper function ? -+ -+Instead of cutting and pasting all over creation -+ -+* Calling XRRGetScreenResources all the time is not going to fly. It -+ takes hundreds of milliseconds ... Even if it didn't, it wouldn't -+ be acceptable to do all those roundtrips. -+ -+ -+=== Some g_prints left -+ -+ -+=== Version check -+ -+Should be (maj > 1) || (maj == 1 && min >= 2) -+ -+ -+=== Grep for TODO -+ -+ -+=== Setup XRRSelectInput() -+ -+ You should call XRRSelectInput() at the same place where you are -+ calling XSelectInput() right now. The right place to handle the -+ XRandr events is the huge switch in gdkevents-x11.c:gdk_event_translate -+ Check out how other extension events are handled there, like -+ XKB, or XFixes. -+ -+ -+=== Lots of variable naming issues, such as act_output and noutput -+ -+=== Needs to select the input, and hook it up to the signal -+ -+=== Add version markers to API -+ -+=== API to turn monitors on and off? -+ -+- DPMS not exposed through randr, maybe should be -+ -+ - DPMS is presumably a property of either an -+ output or a CRTC. Logically it's an output. -+ -+- Need events when DPMS happens. Exposing the "screen saving on" on -+ dbus may not be good enough. -+ -+=== Why does init_multihead_support() start by freeing monitors and -+outputs? -+ -+=== Do we disable Xinerama support entirely when 1.2 is in use? -+ -+=== We should expose information about what parts of the screen monitors -+are viewing. -+ -+=== Make use of the EDID information? -+ -+ -+-- details for X server -- -+ -+In nv driver SorSetOutputProperty should return TRUE for unknown -+properties. (Like the Intel driver does). -+ -+Detecting plugged in -+ -+- Periodically poll -+ - -+ -+ - One ddc probe takes 5 ms, according to a comment in the intel -+ driver. Running this twice a second would mean spending 1% of -+ overall time doing ddc polling, which is almost certainly not -+ acceptable. -+ -+ 1) Async I2C: -+ -+ void I2CProbeAsync(..., callback, data); -+ Bool I2CPending() -+ void I2CUpdate() -+ -+ In Dispatch, call I2CUpdate() -+ Before going idle, do -+ -+ while (I2CPending()) -+ I2CUpdate() -+ -+ Would need -+ RegisterDispatchFunction() (Is this called Wakeup?) -+ RegisterIdleFunction() -+ -+ Note the idle function should have the option of saying: -+ "check if something else happened; if not, call me again" and -+ "ok, I'm done - go idle". Otherwise, we would be blocking for -+ 5 ms whenever the X server went idle. So actually the idle -+ function should be -+ -+ if (I2CPending()) -+ { -+ I2CUpdate(); -+ return TRUE; /* call me again */ -+ } -+ else -+ { -+ return FALSE; /* I'm done */ -+ } -+ -+ What happens if another I2C requests come in while an async one -+ is pending? Most likely we simply finish whatever is going on, -+ then process the new request. -+ -+ What happens if an X request takes so long that we get timeouts on -+ the i2c bus? Good question. Need to read the VESA ddc spec. -+ -+ 2) Run the polling in a separate thread. -+ -+ Probably crack. -+ -+ 3) Run the polling less, maybe once every three seconds. -+ -+-- details for control panel -- -+Screen changes -+ - Currently it is polling via rw_screen_refresh(), which will always emit -+ a screen-changed event. In reponse to this event the capplet currently -+ checks whether anything changed physically about the setup. This means -+ the capplet can't react to external changes to modes. On the other hand -+ if it didn't -+Disallow combinations that would exceed the screen ranges. -+ - Note rotations -+ -+Give rw objects stable positions in memory so that they can be cached -+across screen_changed events. -+ -+Add Clone Mode -+ -+Drag and drop for the monitors -+ - 2 dimensional layout -+ -+Store make and model in monitors.xml, then if serial numbers don't -+match, fall back to a make and model match. Users with an nfs mounted -+home directory should not have to reconfigure for each new system they -+log in to. -+ -+Make sure text is scaled correctly -+ -+Need to sanitize naming -+ RWOutput vs Output - should probably be OutputInfo -+ rate vs. freq - decide on one -+ -+Should probably reconsider the use of null terminated arrays. -+Maybe lists would be better. -+ -+Pick a fixed scale, so that two 1024x768 don't look like two 6x4. -+ - An alternative would be to draw a checkerboard pattern -+ below the monitors. -+ -+ -+ -+done: -+ -+Add rotation -+ -+Disable panel checkbox for now -+ -+Patch into gnome-desktop -+ -+Find out how to share code between gcc and gsd -+ -+Make it assign coordinates correctly -+ - including computing correct screen size -+ -diff --git a/capplets/display/build.sh b/capplets/display/build.sh -new file mode 100644 -index 0000000..4268ab7 ---- /dev/null -+++ b/capplets/display/build.sh -@@ -0,0 +1 @@ -+gcc -g -Wall `pkg-config --cflags --libs gtk+-2.0 libglade-2.0` -I../ ../randrwrap.c ../monitor-db.c xrandr-capplet.c ../edid-parse.c ../display-name.c scrollarea.c foo-marshal.c -o capplet -diff --git a/capplets/display/display-capplet.glade b/capplets/display/display-capplet.glade -new file mode 100644 -index 0000000..5ccc11f ---- /dev/null -+++ b/capplets/display/display-capplet.glade -@@ -0,0 +1,387 @@ -+ -+ -+ -+ -+ -+ -+ 18 -+ Monitor Resolution Settings -+ GTK_WINDOW_TOPLEVEL -+ GTK_WIN_POS_NONE -+ False -+ True -+ False -+ True -+ False -+ False -+ GDK_WINDOW_TYPE_HINT_DIALOG -+ GDK_GRAVITY_NORTH_WEST -+ True -+ False -+ False -+ -+ -+ -+ True -+ False -+ 0 -+ -+ -+ -+ True -+ GTK_BUTTONBOX_END -+ -+ -+ -+ True -+ True -+ True -+ gtk-help -+ True -+ GTK_RELIEF_NORMAL -+ True -+ -11 -+ -+ -+ -+ -+ -+ True -+ True -+ True -+ gtk-apply -+ True -+ GTK_RELIEF_NORMAL -+ True -+ -10 -+ -+ -+ -+ -+ -+ True -+ True -+ True -+ gtk-close -+ True -+ GTK_RELIEF_NORMAL -+ True -+ -7 -+ -+ -+ -+ -+ 0 -+ False -+ True -+ GTK_PACK_END -+ -+ -+ -+ -+ -+ True -+ 0.5 -+ 0.5 -+ 1 -+ 1 -+ 0 -+ 24 -+ 0 -+ 0 -+ -+ -+ -+ True -+ False -+ 12 -+ -+ -+ -+ True -+ True -+ Mirror Screens -+ True -+ GTK_RELIEF_NORMAL -+ True -+ False -+ False -+ True -+ -+ -+ 0 -+ False -+ False -+ -+ -+ -+ -+ -+ True -+ 0.5 -+ 0.5 -+ 1 -+ 1 -+ 0 -+ 0 -+ 0 -+ 0 -+ -+ -+ -+ -+ -+ -+ 0 -+ True -+ True -+ -+ -+ -+ -+ -+ True -+ 0.5 -+ 0.5 -+ 1 -+ 1 -+ 12 -+ 12 -+ 24 -+ 24 -+ -+ -+ -+ True -+ 2 -+ 4 -+ False -+ 12 -+ 12 -+ -+ -+ -+ True -+ _Resolution -+ True -+ False -+ GTK_JUSTIFY_LEFT -+ False -+ False -+ 0 -+ 0.5 -+ 0 -+ 0 -+ PANGO_ELLIPSIZE_NONE -+ -1 -+ False -+ 0 -+ -+ -+ 0 -+ 1 -+ 0 -+ 1 -+ fill -+ -+ -+ -+ -+ -+ -+ True -+ Re_fresh Rate: -+ True -+ False -+ GTK_JUSTIFY_LEFT -+ False -+ False -+ 0 -+ 0.5 -+ 0 -+ 0 -+ PANGO_ELLIPSIZE_NONE -+ -1 -+ False -+ 0 -+ -+ -+ 0 -+ 1 -+ 1 -+ 2 -+ fill -+ -+ -+ -+ -+ -+ -+ True -+ False -+ True -+ -+ -+ 1 -+ 2 -+ 0 -+ 1 -+ fill -+ -+ -+ -+ -+ -+ True -+ False -+ True -+ -+ -+ 1 -+ 2 -+ 1 -+ 2 -+ fill -+ fill -+ -+ -+ -+ -+ -+ True -+ Include _Panel -+ True -+ False -+ GTK_JUSTIFY_LEFT -+ False -+ False -+ 0 -+ 0.5 -+ 0 -+ 0 -+ panel_checkbox -+ PANGO_ELLIPSIZE_NONE -+ -1 -+ False -+ 0 -+ -+ -+ 2 -+ 3 -+ 0 -+ 1 -+ fill -+ -+ -+ -+ -+ -+ -+ True -+ R_otation -+ True -+ False -+ GTK_JUSTIFY_LEFT -+ False -+ False -+ 0 -+ 0.5 -+ 0 -+ 0 -+ PANGO_ELLIPSIZE_NONE -+ -1 -+ False -+ 0 -+ -+ -+ 2 -+ 3 -+ 1 -+ 2 -+ fill -+ -+ -+ -+ -+ -+ -+ True -+ Normal -+Left -+Right -+Upside-down -+ -+ False -+ True -+ -+ -+ 3 -+ 4 -+ 1 -+ 2 -+ fill -+ -+ -+ -+ -+ -+ True -+ True -+ GTK_RELIEF_NORMAL -+ True -+ False -+ False -+ True -+ -+ -+ -+ -+ -+ -+ 3 -+ 4 -+ 0 -+ 1 -+ fill -+ -+ -+ -+ -+ -+ -+ -+ 0 -+ False -+ True -+ -+ -+ -+ -+ -+ True -+ True -+ _Detect Displays -+ True -+ GTK_RELIEF_NORMAL -+ True -+ -+ -+ 0 -+ False -+ False -+ -+ -+ -+ -+ -+ -+ 0 -+ True -+ True -+ -+ -+ -+ -+ -+ -+ -diff --git a/capplets/display/foo-marshal.c b/capplets/display/foo-marshal.c -new file mode 100644 -index 0000000..a40b086 ---- /dev/null -+++ b/capplets/display/foo-marshal.c -@@ -0,0 +1,279 @@ -+ -+#include -+ -+ -+#ifdef G_ENABLE_DEBUG -+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) -+#define g_marshal_value_peek_char(v) g_value_get_char (v) -+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) -+#define g_marshal_value_peek_int(v) g_value_get_int (v) -+#define g_marshal_value_peek_uint(v) g_value_get_uint (v) -+#define g_marshal_value_peek_long(v) g_value_get_long (v) -+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) -+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) -+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) -+#define g_marshal_value_peek_enum(v) g_value_get_enum (v) -+#define g_marshal_value_peek_flags(v) g_value_get_flags (v) -+#define g_marshal_value_peek_float(v) g_value_get_float (v) -+#define g_marshal_value_peek_double(v) g_value_get_double (v) -+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) -+#define g_marshal_value_peek_param(v) g_value_get_param (v) -+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) -+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) -+#define g_marshal_value_peek_object(v) g_value_get_object (v) -+#else /* !G_ENABLE_DEBUG */ -+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. -+ * Do not access GValues directly in your code. Instead, use the -+ * g_value_get_*() functions -+ */ -+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int -+#define g_marshal_value_peek_char(v) (v)->data[0].v_int -+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint -+#define g_marshal_value_peek_int(v) (v)->data[0].v_int -+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint -+#define g_marshal_value_peek_long(v) (v)->data[0].v_long -+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong -+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 -+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 -+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long -+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong -+#define g_marshal_value_peek_float(v) (v)->data[0].v_float -+#define g_marshal_value_peek_double(v) (v)->data[0].v_double -+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer -+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer -+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer -+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer -+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer -+#endif /* !G_ENABLE_DEBUG */ -+ -+ -+/* VOID:OBJECT,OBJECT (marshal.list:1) */ -+void -+foo_marshal_VOID__OBJECT_OBJECT (GClosure *closure, -+ GValue *return_value G_GNUC_UNUSED, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint G_GNUC_UNUSED, -+ gpointer marshal_data) -+{ -+ typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, -+ gpointer arg_1, -+ gpointer arg_2, -+ gpointer data2); -+ register GMarshalFunc_VOID__OBJECT_OBJECT callback; -+ register GCClosure *cc = (GCClosure*) closure; -+ register gpointer data1, data2; -+ -+ g_return_if_fail (n_param_values == 3); -+ -+ if (G_CCLOSURE_SWAP_DATA (closure)) -+ { -+ data1 = closure->data; -+ data2 = g_value_peek_pointer (param_values + 0); -+ } -+ else -+ { -+ data1 = g_value_peek_pointer (param_values + 0); -+ data2 = closure->data; -+ } -+ callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); -+ -+ callback (data1, -+ g_marshal_value_peek_object (param_values + 1), -+ g_marshal_value_peek_object (param_values + 2), -+ data2); -+} -+ -+/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */ -+void -+foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, -+ GValue *return_value G_GNUC_UNUSED, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint G_GNUC_UNUSED, -+ gpointer marshal_data) -+{ -+ typedef void (*GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (gpointer data1, -+ guint arg_1, -+ guint arg_2, -+ guint arg_3, -+ guint arg_4, -+ gpointer data2); -+ register GMarshalFunc_VOID__UINT_UINT_UINT_UINT callback; -+ register GCClosure *cc = (GCClosure*) closure; -+ register gpointer data1, data2; -+ -+ g_return_if_fail (n_param_values == 5); -+ -+ if (G_CCLOSURE_SWAP_DATA (closure)) -+ { -+ data1 = closure->data; -+ data2 = g_value_peek_pointer (param_values + 0); -+ } -+ else -+ { -+ data1 = g_value_peek_pointer (param_values + 0); -+ data2 = closure->data; -+ } -+ callback = (GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (marshal_data ? marshal_data : cc->callback); -+ -+ callback (data1, -+ g_marshal_value_peek_uint (param_values + 1), -+ g_marshal_value_peek_uint (param_values + 2), -+ g_marshal_value_peek_uint (param_values + 3), -+ g_marshal_value_peek_uint (param_values + 4), -+ data2); -+} -+ -+/* VOID:UINT,UINT (marshal.list:3) */ -+void -+foo_marshal_VOID__UINT_UINT (GClosure *closure, -+ GValue *return_value G_GNUC_UNUSED, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint G_GNUC_UNUSED, -+ gpointer marshal_data) -+{ -+ typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer data1, -+ guint arg_1, -+ guint arg_2, -+ gpointer data2); -+ register GMarshalFunc_VOID__UINT_UINT callback; -+ register GCClosure *cc = (GCClosure*) closure; -+ register gpointer data1, data2; -+ -+ g_return_if_fail (n_param_values == 3); -+ -+ if (G_CCLOSURE_SWAP_DATA (closure)) -+ { -+ data1 = closure->data; -+ data2 = g_value_peek_pointer (param_values + 0); -+ } -+ else -+ { -+ data1 = g_value_peek_pointer (param_values + 0); -+ data2 = closure->data; -+ } -+ callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback); -+ -+ callback (data1, -+ g_marshal_value_peek_uint (param_values + 1), -+ g_marshal_value_peek_uint (param_values + 2), -+ data2); -+} -+ -+/* VOID:BOXED (marshal.list:4) */ -+ -+/* VOID:BOXED,BOXED (marshal.list:5) */ -+void -+foo_marshal_VOID__BOXED_BOXED (GClosure *closure, -+ GValue *return_value G_GNUC_UNUSED, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint G_GNUC_UNUSED, -+ gpointer marshal_data) -+{ -+ typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer data1, -+ gpointer arg_1, -+ gpointer arg_2, -+ gpointer data2); -+ register GMarshalFunc_VOID__BOXED_BOXED callback; -+ register GCClosure *cc = (GCClosure*) closure; -+ register gpointer data1, data2; -+ -+ g_return_if_fail (n_param_values == 3); -+ -+ if (G_CCLOSURE_SWAP_DATA (closure)) -+ { -+ data1 = closure->data; -+ data2 = g_value_peek_pointer (param_values + 0); -+ } -+ else -+ { -+ data1 = g_value_peek_pointer (param_values + 0); -+ data2 = closure->data; -+ } -+ callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback); -+ -+ callback (data1, -+ g_marshal_value_peek_boxed (param_values + 1), -+ g_marshal_value_peek_boxed (param_values + 2), -+ data2); -+} -+ -+/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */ -+void -+foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure *closure, -+ GValue *return_value G_GNUC_UNUSED, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint G_GNUC_UNUSED, -+ gpointer marshal_data) -+{ -+ typedef void (*GMarshalFunc_VOID__POINTER_BOXED_POINTER) (gpointer data1, -+ gpointer arg_1, -+ gpointer arg_2, -+ gpointer arg_3, -+ gpointer data2); -+ register GMarshalFunc_VOID__POINTER_BOXED_POINTER callback; -+ register GCClosure *cc = (GCClosure*) closure; -+ register gpointer data1, data2; -+ -+ g_return_if_fail (n_param_values == 4); -+ -+ if (G_CCLOSURE_SWAP_DATA (closure)) -+ { -+ data1 = closure->data; -+ data2 = g_value_peek_pointer (param_values + 0); -+ } -+ else -+ { -+ data1 = g_value_peek_pointer (param_values + 0); -+ data2 = closure->data; -+ } -+ callback = (GMarshalFunc_VOID__POINTER_BOXED_POINTER) (marshal_data ? marshal_data : cc->callback); -+ -+ callback (data1, -+ g_marshal_value_peek_pointer (param_values + 1), -+ g_marshal_value_peek_boxed (param_values + 2), -+ g_marshal_value_peek_pointer (param_values + 3), -+ data2); -+} -+ -+/* VOID:POINTER,POINTER (marshal.list:7) */ -+void -+foo_marshal_VOID__POINTER_POINTER (GClosure *closure, -+ GValue *return_value G_GNUC_UNUSED, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint G_GNUC_UNUSED, -+ gpointer marshal_data) -+{ -+ typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1, -+ gpointer arg_1, -+ gpointer arg_2, -+ gpointer data2); -+ register GMarshalFunc_VOID__POINTER_POINTER callback; -+ register GCClosure *cc = (GCClosure*) closure; -+ register gpointer data1, data2; -+ -+ g_return_if_fail (n_param_values == 3); -+ -+ if (G_CCLOSURE_SWAP_DATA (closure)) -+ { -+ data1 = closure->data; -+ data2 = g_value_peek_pointer (param_values + 0); -+ } -+ else -+ { -+ data1 = g_value_peek_pointer (param_values + 0); -+ data2 = closure->data; -+ } -+ callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); -+ -+ callback (data1, -+ g_marshal_value_peek_pointer (param_values + 1), -+ g_marshal_value_peek_pointer (param_values + 2), -+ data2); -+} -+ -diff --git a/capplets/display/foo-marshal.h b/capplets/display/foo-marshal.h -new file mode 100644 -index 0000000..59d9261 ---- /dev/null -+++ b/capplets/display/foo-marshal.h -@@ -0,0 +1,63 @@ -+ -+#ifndef __foo_marshal_MARSHAL_H__ -+#define __foo_marshal_MARSHAL_H__ -+ -+#include -+ -+G_BEGIN_DECLS -+ -+/* VOID:OBJECT,OBJECT (marshal.list:1) */ -+extern void foo_marshal_VOID__OBJECT_OBJECT (GClosure *closure, -+ GValue *return_value, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint, -+ gpointer marshal_data); -+ -+/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */ -+extern void foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, -+ GValue *return_value, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint, -+ gpointer marshal_data); -+ -+/* VOID:UINT,UINT (marshal.list:3) */ -+extern void foo_marshal_VOID__UINT_UINT (GClosure *closure, -+ GValue *return_value, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint, -+ gpointer marshal_data); -+ -+/* VOID:BOXED (marshal.list:4) */ -+#define foo_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED -+ -+/* VOID:BOXED,BOXED (marshal.list:5) */ -+extern void foo_marshal_VOID__BOXED_BOXED (GClosure *closure, -+ GValue *return_value, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint, -+ gpointer marshal_data); -+ -+/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */ -+extern void foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure *closure, -+ GValue *return_value, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint, -+ gpointer marshal_data); -+ -+/* VOID:POINTER,POINTER (marshal.list:7) */ -+extern void foo_marshal_VOID__POINTER_POINTER (GClosure *closure, -+ GValue *return_value, -+ guint n_param_values, -+ const GValue *param_values, -+ gpointer invocation_hint, -+ gpointer marshal_data); -+ -+G_END_DECLS -+ -+#endif /* __foo_marshal_MARSHAL_H__ */ -+ -diff --git a/capplets/display/scrollarea.c b/capplets/display/scrollarea.c -new file mode 100644 -index 0000000..3d3d8b6 ---- /dev/null -+++ b/capplets/display/scrollarea.c -@@ -0,0 +1,1920 @@ -+/* Copyright 2006, 2007, 2008, Soren Sandmann -+ * -+ * 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 of the License, or (at your option) any later version. -+ * -+ * This library 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 -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, -+ * Boston, MA 02111-1307, USA. -+ */ -+ -+#include /* For GDK_PARENT_RELATIVE_BG */ -+#include "scrollarea.h" -+#include "foo-marshal.h" -+ -+G_DEFINE_TYPE (FooScrollArea, foo_scroll_area, GTK_TYPE_CONTAINER); -+ -+static GtkWidgetClass *parent_class; -+ -+typedef struct BackingStore BackingStore; -+ -+typedef void (* ExposeFunc) (cairo_t *cr, GdkRegion *region, gpointer data); -+ -+#if 0 -+static void backing_store_draw (BackingStore *store, -+ GdkDrawable *dest, -+ GdkRegion *clip, -+ int dest_x, -+ int dest_y); -+static void backing_store_scroll (BackingStore *store, -+ int dx, int dy); -+static void backing_store_invalidate_rect (BackingStore *store, -+ GdkRectangle *rect); -+static void backing_store_invalidate_region (BackingStore *store, -+ GdkRegion *region); -+static void backing_store_invalidate_all (BackingStore *store); -+static BackingStore *backing_store_new (GdkWindow *window, -+ int width, int height); -+static void backing_store_resize (BackingStore *store, -+ int width, int height); -+static void backing_store_process_updates (BackingStore *store, -+ ExposeFunc func, -+ gpointer data); -+static void backing_store_free (BackingStore *store); -+#endif -+ -+typedef struct InputPath InputPath; -+typedef struct InputRegion InputRegion; -+typedef struct AutoScrollInfo AutoScrollInfo; -+ -+struct InputPath -+{ -+ gboolean is_stroke; -+ cairo_fill_rule_t fill_rule; -+ double line_width; -+ cairo_path_t *path; /* In canvas coordinates */ -+ -+ FooScrollAreaEventFunc func; -+ gpointer data; -+ -+ InputPath *next; -+}; -+ -+/* InputRegions are mutually disjoint */ -+struct InputRegion -+{ -+ GdkRegion *region; /* the boundary of this area in canvas coordinates */ -+ InputPath *paths; -+}; -+ -+struct AutoScrollInfo -+{ -+ int dx; -+ int dy; -+ int timeout_id; -+ int begin_x; -+ int begin_y; -+ double res_x; -+ double res_y; -+ GTimer *timer; -+}; -+ -+struct FooScrollAreaPrivate -+{ -+ GdkWindow *input_window; -+ -+ int width; -+ int height; -+ -+ GtkAdjustment *hadj; -+ GtkAdjustment *vadj; -+ int x_offset; -+ int y_offset; -+ -+ int min_width; -+ int min_height; -+ -+ GPtrArray *input_regions; -+ -+ AutoScrollInfo *auto_scroll_info; -+ -+ /* During expose, this region is set to the region -+ * being exposed. At other times, it is NULL -+ * -+ * It is used for clipping of input areas -+ */ -+ GdkRegion *expose_region; -+ InputRegion *current_input; -+ -+ gboolean grabbed; -+ FooScrollAreaEventFunc grab_func; -+ gpointer grab_data; -+ -+ GdkPixmap *pixmap; -+ GdkRegion *update_region; /* In canvas coordinates */ -+}; -+ -+enum -+{ -+ VIEWPORT_CHANGED, -+ PAINT, -+ INPUT, -+ LAST_SIGNAL, -+}; -+ -+static guint signals [LAST_SIGNAL] = { 0 }; -+ -+static void foo_scroll_area_size_request (GtkWidget *widget, -+ GtkRequisition *requisition); -+static gboolean foo_scroll_area_expose (GtkWidget *widget, -+ GdkEventExpose *expose); -+static void foo_scroll_area_size_allocate (GtkWidget *widget, -+ GtkAllocation *allocation); -+static void foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area, -+ GtkAdjustment *hadjustment, -+ GtkAdjustment *vadjustment); -+static void foo_scroll_area_realize (GtkWidget *widget); -+static void foo_scroll_area_unrealize (GtkWidget *widget); -+static void foo_scroll_area_map (GtkWidget *widget); -+static void foo_scroll_area_unmap (GtkWidget *widget); -+static gboolean foo_scroll_area_button_press (GtkWidget *widget, -+ GdkEventButton *event); -+static gboolean foo_scroll_area_button_release (GtkWidget *widget, -+ GdkEventButton *event); -+static gboolean foo_scroll_area_motion (GtkWidget *widget, -+ GdkEventMotion *event); -+ -+static void -+foo_scroll_area_map (GtkWidget *widget) -+{ -+ FooScrollArea *area = FOO_SCROLL_AREA (widget); -+ -+ GTK_WIDGET_CLASS (parent_class)->map (widget); -+ -+ if (area->priv->input_window) -+ gdk_window_show (area->priv->input_window); -+} -+ -+static void -+foo_scroll_area_unmap (GtkWidget *widget) -+{ -+ FooScrollArea *area = FOO_SCROLL_AREA (widget); -+ -+ if (area->priv->input_window) -+ gdk_window_hide (area->priv->input_window); -+ -+ GTK_WIDGET_CLASS (parent_class)->unmap (widget); -+} -+ -+static void -+foo_scroll_area_finalize (GObject *object) -+{ -+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (object); -+ -+ g_object_unref (scroll_area->priv->hadj); -+ g_object_unref (scroll_area->priv->vadj); -+ -+ g_ptr_array_free (scroll_area->priv->input_regions, TRUE); -+ -+ g_free (scroll_area->priv); -+ -+ G_OBJECT_CLASS (foo_scroll_area_parent_class)->finalize (object); -+} -+ -+static void -+foo_scroll_area_class_init (FooScrollAreaClass *class) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (class); -+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); -+ -+ object_class->finalize = foo_scroll_area_finalize; -+ widget_class->size_request = foo_scroll_area_size_request; -+ widget_class->expose_event = foo_scroll_area_expose; -+ widget_class->size_allocate = foo_scroll_area_size_allocate; -+ widget_class->realize = foo_scroll_area_realize; -+ widget_class->unrealize = foo_scroll_area_unrealize; -+ widget_class->button_press_event = foo_scroll_area_button_press; -+ widget_class->button_release_event = foo_scroll_area_button_release; -+ widget_class->motion_notify_event = foo_scroll_area_motion; -+ widget_class->map = foo_scroll_area_map; -+ widget_class->unmap = foo_scroll_area_unmap; -+ -+ class->set_scroll_adjustments = foo_scroll_area_set_scroll_adjustments; -+ -+ parent_class = g_type_class_peek_parent (class); -+ -+ signals[VIEWPORT_CHANGED] = -+ g_signal_new ("viewport_changed", -+ G_OBJECT_CLASS_TYPE (object_class), -+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, -+ G_STRUCT_OFFSET (FooScrollAreaClass, -+ viewport_changed), -+ NULL, NULL, -+ foo_marshal_VOID__BOXED_BOXED, -+ G_TYPE_NONE, 2, -+ GDK_TYPE_RECTANGLE, -+ GDK_TYPE_RECTANGLE); -+ -+ signals[PAINT] = -+ g_signal_new ("paint", -+ G_OBJECT_CLASS_TYPE (object_class), -+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, -+ G_STRUCT_OFFSET (FooScrollAreaClass, -+ paint), -+ NULL, NULL, -+ foo_marshal_VOID__POINTER_BOXED_POINTER, -+ G_TYPE_NONE, 3, -+ G_TYPE_POINTER, -+ GDK_TYPE_RECTANGLE, -+ G_TYPE_POINTER); -+ -+ widget_class->set_scroll_adjustments_signal = -+ g_signal_new ("set_scroll_adjustments", -+ G_OBJECT_CLASS_TYPE (object_class), -+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, -+ G_STRUCT_OFFSET (FooScrollAreaClass, -+ set_scroll_adjustments), -+ NULL, NULL, -+ foo_marshal_VOID__OBJECT_OBJECT, -+ G_TYPE_NONE, 2, -+ GTK_TYPE_ADJUSTMENT, -+ GTK_TYPE_ADJUSTMENT); -+} -+ -+static GtkAdjustment * -+new_adjustment (void) -+{ -+ return GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); -+} -+ -+static void -+foo_scroll_area_init (FooScrollArea *scroll_area) -+{ -+ GTK_WIDGET_SET_FLAGS (scroll_area, GTK_NO_WINDOW); -+ -+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (scroll_area), FALSE); -+ -+ scroll_area->priv = g_new0 (FooScrollAreaPrivate, 1); -+ scroll_area->priv->width = 0; -+ scroll_area->priv->height = 0; -+ scroll_area->priv->hadj = g_object_ref_sink (new_adjustment()); -+ scroll_area->priv->vadj = g_object_ref_sink (new_adjustment()); -+ scroll_area->priv->x_offset = 0.0; -+ scroll_area->priv->y_offset = 0.0; -+ scroll_area->priv->min_width = -1; -+ scroll_area->priv->min_height = -1; -+ scroll_area->priv->auto_scroll_info = NULL; -+ scroll_area->priv->input_regions = g_ptr_array_new (); -+ scroll_area->priv->pixmap = NULL; -+ scroll_area->priv->update_region = gdk_region_new (); -+ -+ gtk_widget_set_double_buffered (GTK_WIDGET (scroll_area), FALSE); -+} -+ -+static void -+translate_cairo_device (cairo_t *cr, -+ int x_offset, -+ int y_offset) -+{ -+ cairo_surface_t *surface = cairo_get_target (cr); -+ double dev_x; -+ double dev_y; -+ -+ cairo_surface_get_device_offset (surface, &dev_x, &dev_y); -+ dev_x += x_offset; -+ dev_y += y_offset; -+ cairo_surface_set_device_offset (surface, dev_x, dev_y); -+} -+ -+#if 0 -+static void -+print_region (const char *header, GdkRegion *region) -+{ -+ GdkRectangle *rects; -+ int n_rects; -+ int i; -+ -+ g_print ("%s\n", header); -+ -+ gdk_region_get_rectangles (region, &rects, &n_rects); -+ for (i = 0; i < n_rects; ++i) -+ { -+ GdkRectangle *rect = &(rects[i]); -+ g_print (" %d %d %d %d\n", -+ rect->x, rect->y, rect->width, rect->height); -+ } -+} -+#endif -+ -+typedef void (* PathForeachFunc) (double *x, -+ double *y, -+ gpointer data); -+ -+static void -+path_foreach_point (cairo_path_t *path, -+ PathForeachFunc func, -+ gpointer user_data) -+{ -+ int i; -+ -+ for (i = 0; i < path->num_data; i += path->data[i].header.length) -+ { -+ cairo_path_data_t *data = &(path->data[i]); -+ -+ switch (data->header.type) -+ { -+ case CAIRO_PATH_MOVE_TO: -+ case CAIRO_PATH_LINE_TO: -+ func (&(data[1].point.x), &(data[1].point.y), user_data); -+ break; -+ -+ case CAIRO_PATH_CURVE_TO: -+ func (&(data[1].point.x), &(data[1].point.y), user_data); -+ func (&(data[2].point.x), &(data[2].point.y), user_data); -+ func (&(data[3].point.x), &(data[3].point.y), user_data); -+ break; -+ -+ case CAIRO_PATH_CLOSE_PATH: -+ break; -+ } -+ } -+} -+ -+typedef struct -+{ -+ double x1, y1, x2, y2; -+} Box; -+ -+#if 0 -+static void -+update_box (double *x, double *y, gpointer data) -+{ -+ Box *box = data; -+ -+ if (*x < box->x1) -+ box->x1 = *x; -+ -+ if (*y < box->y1) -+ box->y1 = *y; -+ -+ if (*y > box->y2) -+ box->y2 = *y; -+ -+ if (*x > box->x2) -+ box->x2 = *x; -+} -+#endif -+ -+#if 0 -+static void -+path_compute_extents (cairo_path_t *path, -+ GdkRectangle *rect) -+{ -+ if (rect) -+ { -+ Box box = { G_MAXDOUBLE, G_MAXDOUBLE, G_MINDOUBLE, G_MINDOUBLE }; -+ -+ path_foreach_point (path, update_box, &box); -+ -+ rect->x = box.x1; -+ rect->y = box.y1; -+ rect->width = box.x2 - box.x1; -+ rect->height = box.y2 - box.y1; -+ } -+} -+#endif -+ -+static void -+input_path_free_list (InputPath *paths) -+{ -+ if (!paths) -+ return; -+ -+ input_path_free_list (paths->next); -+ cairo_path_destroy (paths->path); -+ g_free (paths); -+} -+ -+static void -+input_region_free (InputRegion *region) -+{ -+ input_path_free_list (region->paths); -+ gdk_region_destroy (region->region); -+ -+ g_free (region); -+} -+ -+static void -+get_viewport (FooScrollArea *scroll_area, -+ GdkRectangle *viewport) -+{ -+ GtkWidget *widget = GTK_WIDGET (scroll_area); -+ -+ viewport->x = scroll_area->priv->x_offset; -+ viewport->y = scroll_area->priv->y_offset; -+ viewport->width = widget->allocation.width; -+ viewport->height = widget->allocation.height; -+} -+ -+static void -+allocation_to_canvas (FooScrollArea *area, -+ int *x, -+ int *y) -+{ -+ *x += area->priv->x_offset; -+ *y += area->priv->y_offset; -+} -+ -+static void -+clear_exposed_input_region (FooScrollArea *area, -+ GdkRegion *exposed) /* in canvas coordinates */ -+{ -+ int i; -+ GdkRegion *viewport; -+ GdkRectangle allocation; -+ -+ allocation = GTK_WIDGET (area)->allocation; -+ allocation.x = 0; -+ allocation.y = 0; -+ allocation_to_canvas (area, &allocation.x, &allocation.y); -+ viewport = gdk_region_rectangle (&allocation); -+ gdk_region_subtract (viewport, exposed); -+ -+ for (i = 0; i < area->priv->input_regions->len; ++i) -+ { -+ InputRegion *region = area->priv->input_regions->pdata[i]; -+ -+ gdk_region_intersect (region->region, viewport); -+ -+ if (gdk_region_empty (region->region)) -+ { -+ input_region_free (region); -+ g_ptr_array_remove_index_fast (area->priv->input_regions, i--); -+ } -+ } -+ -+ gdk_region_destroy (viewport); -+ -+#if 0 -+ path = region->paths; -+ while (path != NULL) -+ { -+ GdkRectangle rect; -+ -+ path_compute_extents (path->path, &rect); -+ -+ if (gdk_region_rect_in (area->priv->expose_region, &rect) == GDK_OVERLAP_RECTANGLE_IN) -+ g_print ("we would have deleted it\n"); -+#if 0 -+ else -+ g_print ("nope (%d %d %d %d)\n", ); -+#endif -+ -+ path = path->next; -+ } -+ -+ /* FIXME: we should also delete paths (and path segments) -+ * completely contained in the expose_region -+ */ -+ } -+#endif -+} -+ -+static void -+setup_background_cr (GdkWindow *window, -+ cairo_t *cr, -+ int x_offset, -+ int y_offset) -+{ -+ GdkWindowObject *private = (GdkWindowObject *)window; -+ -+ if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent) -+ { -+ x_offset += private->x; -+ y_offset += private->y; -+ -+ setup_background_cr (GDK_WINDOW (private->parent), cr, x_offset, y_offset); -+ } -+ else if (private->bg_pixmap && -+ private->bg_pixmap != GDK_PARENT_RELATIVE_BG && -+ private->bg_pixmap != GDK_NO_BG) -+ { -+ gdk_cairo_set_source_pixmap (cr, private->bg_pixmap, -x_offset, -y_offset); -+ } -+ else -+ { -+ gdk_cairo_set_source_color (cr, &private->bg_color); -+ } -+} -+ -+static void -+initialize_background (GtkWidget *widget, -+ cairo_t *cr) -+{ -+ setup_background_cr (widget->window, cr, 0, 0); -+ -+ cairo_paint (cr); -+} -+ -+static void -+clip_to_region (cairo_t *cr, GdkRegion *region) -+{ -+ int n_rects; -+ GdkRectangle *rects; -+ -+ gdk_region_get_rectangles (region, &rects, &n_rects); -+ -+ cairo_new_path (cr); -+ while (n_rects--) -+ { -+ GdkRectangle *rect = &(rects[n_rects]); -+ -+ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height); -+ } -+ cairo_clip (cr); -+ -+ g_free (rects); -+} -+ -+static void -+simple_draw_drawable (GdkDrawable *dst, -+ GdkDrawable *src, -+ int src_x, -+ int src_y, -+ int dst_x, -+ int dst_y, -+ int width, -+ int height) -+{ -+ GdkGC *gc = gdk_gc_new (dst); -+ -+ gdk_draw_drawable (dst, gc, src, src_x, src_y, dst_x, dst_y, width, height); -+ -+ g_object_unref (gc); -+} -+ -+static gboolean -+foo_scroll_area_expose (GtkWidget *widget, -+ GdkEventExpose *expose) -+{ -+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget); -+ cairo_t *cr; -+ GdkRectangle extents; -+ GdkRegion *region; -+ int x_offset, y_offset; -+ GdkGC *gc; -+ -+ /* I don't think expose can ever recurse for the same area */ -+ g_assert (!scroll_area->priv->expose_region); -+ -+ /* Note that this function can be called at a time -+ * where the adj->value is different from x_offset. -+ * Ie., the GtkScrolledWindow changed the adj->value -+ * without emitting the value_changed signal. -+ * -+ * Hence we must always use the value we got -+ * the last time the signal was emitted, ie., -+ * priv->{x,y}_offset. -+ */ -+ -+ x_offset = scroll_area->priv->x_offset; -+ y_offset = scroll_area->priv->y_offset; -+ -+ scroll_area->priv->expose_region = expose->region; -+ -+ /* Setup input areas */ -+ clear_exposed_input_region (scroll_area, scroll_area->priv->update_region); -+ -+ scroll_area->priv->current_input = g_new0 (InputRegion, 1); -+ scroll_area->priv->current_input->region = gdk_region_copy (scroll_area->priv->update_region); -+ scroll_area->priv->current_input->paths = NULL; -+ g_ptr_array_add (scroll_area->priv->input_regions, -+ scroll_area->priv->current_input); -+ -+ region = scroll_area->priv->update_region; -+ scroll_area->priv->update_region = gdk_region_new (); -+ -+ /* Create cairo context */ -+ cr = gdk_cairo_create (scroll_area->priv->pixmap); -+ translate_cairo_device (cr, -x_offset, -y_offset); -+ clip_to_region (cr, region); -+ initialize_background (widget, cr); -+ -+ /* Create regions */ -+ gdk_region_get_clipbox (region, &extents); -+ -+ g_signal_emit (widget, signals[PAINT], 0, cr, &extents, region); -+ -+ /* Destroy stuff */ -+ cairo_destroy (cr); -+ -+ scroll_area->priv->expose_region = NULL; -+ scroll_area->priv->current_input = NULL; -+ -+ /* Finally draw the backing pixmap */ -+ gc = gdk_gc_new (widget->window); -+ -+ gdk_gc_set_clip_region (gc, expose->region); -+ -+ gdk_draw_drawable (widget->window, gc, scroll_area->priv->pixmap, -+ 0, 0, widget->allocation.x, widget->allocation.y, -+ widget->allocation.width, widget->allocation.height); -+ -+ g_object_unref (gc); -+ gdk_region_destroy (region); -+ -+ return TRUE; -+} -+ -+void -+foo_scroll_area_get_viewport (FooScrollArea *scroll_area, -+ GdkRectangle *viewport) -+{ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); -+ -+ if (!viewport) -+ return; -+ -+ get_viewport (scroll_area, viewport); -+} -+ -+static void -+process_event (FooScrollArea *scroll_area, -+ FooScrollAreaEventType input_type, -+ int x, -+ int y); -+ -+static void -+emit_viewport_changed (FooScrollArea *scroll_area, -+ GdkRectangle *new_viewport, -+ GdkRectangle *old_viewport) -+{ -+ int px, py; -+ g_signal_emit (scroll_area, signals[VIEWPORT_CHANGED], 0, -+ new_viewport, old_viewport); -+ -+ gdk_window_get_pointer (scroll_area->priv->input_window, &px, &py, NULL); -+ -+#if 0 -+ g_print ("procc\n"); -+#endif -+ -+ process_event (scroll_area, FOO_MOTION, px, py); -+} -+ -+static void -+clamp_adjustment (GtkAdjustment *adj) -+{ -+ double old_value = adj->value; -+ -+ if (adj->upper >= adj->page_size) -+ adj->value = CLAMP (adj->value, 0.0, adj->upper - adj->page_size); -+ else -+ adj->value = 0.0; -+ -+ if (old_value != adj->value) -+ gtk_adjustment_value_changed (adj); -+ -+ gtk_adjustment_changed (adj); -+} -+ -+static gboolean -+set_adjustment_values (FooScrollArea *scroll_area) -+{ -+ GtkAllocation *allocation = >K_WIDGET (scroll_area)->allocation; -+ -+ GtkAdjustment *hadj = scroll_area->priv->hadj; -+ GtkAdjustment *vadj = scroll_area->priv->vadj; -+ -+ /* Horizontal */ -+ hadj->page_size = allocation->width; -+ hadj->step_increment = 0.1 * allocation->width; -+ hadj->page_increment = 0.9 * allocation->width; -+ hadj->lower = 0.0; -+ hadj->upper = scroll_area->priv->width; -+ -+ /* Vertical */ -+ vadj->page_size = allocation->height; -+ vadj->step_increment = 0.1 * allocation->height; -+ vadj->page_increment = 0.9 * allocation->height; -+ vadj->lower = 0.0; -+ vadj->upper = scroll_area->priv->height; -+ -+ clamp_adjustment (hadj); -+ clamp_adjustment (vadj); -+ -+ return TRUE; -+} -+ -+static void -+foo_scroll_area_realize (GtkWidget *widget) -+{ -+ FooScrollArea *area = FOO_SCROLL_AREA (widget); -+ GdkWindowAttr attributes; -+ gint attributes_mask; -+ -+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); -+ -+ attributes.window_type = GDK_WINDOW_CHILD; -+ attributes.x = widget->allocation.x; -+ attributes.y = widget->allocation.y; -+ attributes.width = widget->allocation.width; -+ attributes.height = widget->allocation.height; -+ attributes.wclass = GDK_INPUT_ONLY; -+ attributes.event_mask = gtk_widget_get_events (widget); -+ attributes.event_mask |= (GDK_BUTTON_PRESS_MASK | -+ GDK_BUTTON_RELEASE_MASK | -+ GDK_BUTTON1_MOTION_MASK | -+ GDK_BUTTON2_MOTION_MASK | -+ GDK_BUTTON3_MOTION_MASK | -+ GDK_POINTER_MOTION_MASK | -+ GDK_ENTER_NOTIFY_MASK | -+ GDK_LEAVE_NOTIFY_MASK); -+ -+ attributes_mask = GDK_WA_X | GDK_WA_Y; -+ -+ widget->window = gtk_widget_get_parent_window (widget); -+ g_object_ref (widget->window); -+ -+ area->priv->input_window = gdk_window_new (widget->window, -+ &attributes, attributes_mask); -+ area->priv->pixmap = gdk_pixmap_new (widget->window, -+ widget->allocation.width, -+ widget->allocation.height, -+ -1); -+ gdk_window_set_user_data (area->priv->input_window, area); -+ -+ widget->style = gtk_style_attach (widget->style, widget->window); -+} -+ -+static void -+foo_scroll_area_unrealize (GtkWidget *widget) -+{ -+ FooScrollArea *area = FOO_SCROLL_AREA (widget); -+ -+ if (area->priv->input_window) -+ { -+ gdk_window_set_user_data (area->priv->input_window, NULL); -+ gdk_window_destroy (area->priv->input_window); -+ area->priv->input_window = NULL; -+ } -+ -+ GTK_WIDGET_CLASS (parent_class)->unrealize (widget); -+} -+ -+static GdkPixmap * -+create_new_pixmap (GtkWidget *widget, -+ GdkPixmap *old) -+{ -+ GdkPixmap *new = gdk_pixmap_new (widget->window, -+ widget->allocation.width, -+ widget->allocation.height, -+ -1); -+ -+ /* Unfortunately we don't know in which direction we were resized, -+ * so we just assume we were dragged from the south-east corner. -+ * -+ * Although, maybe we could get the root coordinates of the input-window? -+ * That might just work, actually. We need to make sure metacity uses -+ * static gravity for the window before this will be useful. -+ */ -+ simple_draw_drawable (new, old, 0, 0, 0, 0, -1, -1); -+ -+ return new; -+} -+ -+static void -+allocation_to_canvas_region (FooScrollArea *area, -+ GdkRegion *region) -+{ -+ gdk_region_offset (region, area->priv->x_offset, area->priv->y_offset); -+} -+ -+ -+static void -+foo_scroll_area_size_allocate (GtkWidget *widget, -+ GtkAllocation *allocation) -+{ -+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget); -+ GdkRectangle new_viewport; -+ GdkRectangle old_viewport; -+ GdkRegion *old_allocation; -+ GdkRegion *invalid; -+ -+ get_viewport (scroll_area, &old_viewport); -+ -+ old_allocation = gdk_region_rectangle (&widget->allocation); -+ gdk_region_offset (old_allocation, -+ -widget->allocation.x, -widget->allocation.y); -+ invalid = gdk_region_rectangle (allocation); -+ gdk_region_offset (invalid, -allocation->x, -allocation->y); -+ gdk_region_subtract (invalid, old_allocation); -+ allocation_to_canvas_region (scroll_area, invalid); -+ foo_scroll_area_invalidate_region (scroll_area, invalid); -+ gdk_region_destroy (old_allocation); -+ gdk_region_destroy (invalid); -+ -+ widget->allocation = *allocation; -+ -+ if (scroll_area->priv->input_window) -+ { -+ GdkPixmap *new_pixmap; -+ -+ gdk_window_move_resize (scroll_area->priv->input_window, -+ allocation->x, allocation->y, -+ allocation->width, allocation->height); -+ -+ new_pixmap = create_new_pixmap (widget, scroll_area->priv->pixmap); -+ -+ g_object_unref (scroll_area->priv->pixmap); -+ -+ scroll_area->priv->pixmap = new_pixmap; -+ } -+ -+ get_viewport (scroll_area, &new_viewport); -+ -+ emit_viewport_changed (scroll_area, &new_viewport, &old_viewport); -+} -+ -+static void -+emit_input (FooScrollArea *scroll_area, -+ FooScrollAreaEventType type, -+ int x, -+ int y, -+ FooScrollAreaEventFunc func, -+ gpointer data) -+{ -+ FooScrollAreaEvent event; -+ -+ if (!func) -+ return; -+ -+ if (type != FOO_MOTION) -+ emit_input (scroll_area, FOO_MOTION, x, y, func, data); -+ -+#if 0 -+ x += scroll_area->priv->x_offset; -+ y += scroll_area->priv->y_offset; -+#endif -+ -+ event.type = type; -+ event.x = x; -+ event.y = y; -+ -+ func (scroll_area, &event, data); -+} -+ -+#if 0 -+static void -+print_path (const char *header, -+ cairo_path_t *path) -+{ -+ int i; -+ -+ g_print ("%s\n", header); -+ -+ for (i=0; i < path->num_data; i += path->data[i].header.length) -+ { -+ cairo_path_data_t *data = &(path->data[i]); -+ -+ switch (data->header.type) -+ { -+ case CAIRO_PATH_MOVE_TO: -+ g_print ("move to: %f, %f\n", data[1].point.x, data[1].point.y); -+ break; -+ -+ case CAIRO_PATH_LINE_TO: -+ g_print ("line to: %f, %f\n", data[1].point.x, data[1].point.y); -+ break; -+ -+ case CAIRO_PATH_CURVE_TO: -+ g_print ("curve to: %f, %f\n", data[1].point.x, data[1].point.y); -+ g_print (" %f, %f\n", data[1].point.x, data[1].point.y); -+ g_print (" %f, %f\n", data[1].point.x, data[1].point.y); -+ break; -+ -+ case CAIRO_PATH_CLOSE_PATH: -+ break; -+ } -+ } -+} -+#endif -+ -+static void -+process_event (FooScrollArea *scroll_area, -+ FooScrollAreaEventType input_type, -+ int x, -+ int y) -+{ -+ GtkWidget *widget = GTK_WIDGET (scroll_area); -+ int i; -+ -+ allocation_to_canvas (scroll_area, &x, &y); -+ -+ if (scroll_area->priv->grabbed) -+ { -+ emit_input (scroll_area, input_type, x, y, -+ scroll_area->priv->grab_func, -+ scroll_area->priv->grab_data); -+ return; -+ } -+ -+ -+#if 0 -+ x += widget->allocation.x; -+ y += widget->allocation.y; -+#endif -+ -+#if 0 -+ g_print ("number of input regions: %d\n", scroll_area->priv->input_regions->len); -+#endif -+ -+ for (i = 0; i < scroll_area->priv->input_regions->len; ++i) -+ { -+ InputRegion *region = scroll_area->priv->input_regions->pdata[i]; -+ -+#if 0 -+ g_print ("%d ", i); -+ print_region ("region:", region->region); -+#endif -+ -+ if (gdk_region_point_in (region->region, x, y)) -+ { -+ InputPath *path; -+ -+ path = region->paths; -+ while (path) -+ { -+ cairo_t *cr; -+ gboolean inside; -+ -+ cr = gdk_cairo_create (widget->window); -+ cairo_set_fill_rule (cr, path->fill_rule); -+ cairo_set_line_width (cr, path->line_width); -+ cairo_append_path (cr, path->path); -+ -+ if (path->is_stroke) -+ inside = cairo_in_stroke (cr, x, y); -+ else -+ inside = cairo_in_fill (cr, x, y); -+ -+ cairo_destroy (cr); -+ -+ if (inside) -+ { -+ emit_input (scroll_area, input_type, -+ x, y, -+ path->func, -+ path->data); -+ return; -+ } -+ -+ path = path->next; -+ } -+ -+ /* Since the regions are all disjoint, no other region -+ * can match. Of course we could be clever and try and -+ * sort the regions, but so far I have been unable to -+ * make this loop show up on a profile. -+ */ -+ return; -+ } -+ } -+} -+ -+static void -+process_gdk_event (FooScrollArea *scroll_area, -+ int x, -+ int y, -+ GdkEvent *event) -+{ -+ FooScrollAreaEventType input_type; -+ -+ if (event->type == GDK_BUTTON_PRESS) -+ input_type = FOO_BUTTON_PRESS; -+ else if (event->type == GDK_BUTTON_RELEASE) -+ input_type = FOO_BUTTON_RELEASE; -+ else if (event->type == GDK_MOTION_NOTIFY) -+ input_type = FOO_MOTION; -+ else -+ return; -+ -+ process_event (scroll_area, input_type, x, y); -+} -+ -+static gboolean -+foo_scroll_area_button_press (GtkWidget *widget, -+ GdkEventButton *event) -+{ -+ FooScrollArea *area = FOO_SCROLL_AREA (widget); -+ -+ process_gdk_event (area, event->x, event->y, (GdkEvent *)event); -+ -+ return TRUE; -+} -+ -+static gboolean -+foo_scroll_area_button_release (GtkWidget *widget, -+ GdkEventButton *event) -+{ -+ FooScrollArea *area = FOO_SCROLL_AREA (widget); -+ -+ process_gdk_event (area, event->x, event->y, (GdkEvent *)event); -+ -+ return FALSE; -+} -+ -+static gboolean -+foo_scroll_area_motion (GtkWidget *widget, -+ GdkEventMotion *event) -+{ -+ FooScrollArea *area = FOO_SCROLL_AREA (widget); -+ -+ process_gdk_event (area, event->x, event->y, (GdkEvent *)event); -+ return TRUE; -+} -+ -+void -+foo_scroll_area_set_size_fixed_y (FooScrollArea *scroll_area, -+ int width, -+ int height, -+ int old_y, -+ int new_y) -+{ -+ int dy = new_y - old_y; -+ -+ scroll_area->priv->width = width; -+ scroll_area->priv->height = height; -+ -+#if 0 -+ g_print ("diff: %d\n", new_y - old_y); -+#endif -+ -+ scroll_area->priv->vadj->value += dy; -+ -+ set_adjustment_values (scroll_area); -+ -+ if (dy != 0) -+ gtk_adjustment_value_changed (scroll_area->priv->vadj); -+} -+ -+void -+foo_scroll_area_set_size (FooScrollArea *scroll_area, -+ int width, -+ int height) -+{ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); -+ -+ /* FIXME: Default scroll algorithm should probably be to -+ * keep the same *area* outside the screen as before. -+ * -+ * For wrapper widgets that will do something roughly -+ * right. For widgets that don't change size, it -+ * will do the right thing. Except for idle-layouting -+ * widgets. -+ * -+ * Maybe there should be some generic support for those -+ * widgets. Can that even be done? -+ * -+ * Should we have a version of this function using -+ * fixed points? -+ */ -+ -+ scroll_area->priv->width = width; -+ scroll_area->priv->height = height; -+ -+ set_adjustment_values (scroll_area); -+} -+ -+static void -+foo_scroll_area_size_request (GtkWidget *widget, -+ GtkRequisition *requisition) -+{ -+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget); -+ -+ requisition->width = scroll_area->priv->min_width; -+ requisition->height = scroll_area->priv->min_height; -+ -+#if 0 -+ g_print ("request %d %d\n", requisition->width, requisition->height); -+#endif -+} -+ -+#if 0 -+static void -+translate_point (double *x, double *y, gpointer data) -+{ -+ int *translation = data; -+ -+ *x += translation[0]; -+ *y += translation[1]; -+} -+#endif -+ -+#if 0 -+static void -+path_translate (cairo_path_t *path, -+ int dx, -+ int dy) -+{ -+ int translation[2] = {dx, dy}; -+ -+ path_foreach_point (path, translate_point, translation); -+} -+#endif -+ -+static void -+translate_input_regions (FooScrollArea *scroll_area, -+ int dx, -+ int dy) -+{ -+#if 0 -+ int i; -+ -+ for (i = 0; i < scroll_area->priv->input_regions->len; ++i) -+ { -+ InputRegion *region = scroll_area->priv->input_regions->pdata[i]; -+ InputPath *path; -+ -+ gdk_region_offset (region->region, dx, dy); -+ -+ path = region->paths; -+ while (path != NULL) -+ { -+ path_translate (path->path, dx, dy); -+ path = path->next; -+ } -+ } -+#endif -+} -+ -+#if 0 -+static void -+paint_region (FooScrollArea *area, GdkRegion *region) -+{ -+ int n_rects; -+ GdkRectangle *rects; -+ region = gdk_region_copy (region); -+ -+ gdk_region_get_rectangles (region, &rects, &n_rects); -+ -+ gdk_region_offset (region, -+ GTK_WIDGET (area)->allocation.x, -+ GTK_WIDGET (area)->allocation.y); -+ -+ GdkGC *gc = gdk_gc_new (GTK_WIDGET (area)->window); -+ gdk_gc_set_clip_region (gc, region); -+ gdk_draw_rectangle (GTK_WIDGET (area)->window, gc, TRUE, 0, 0, -1, -1); -+ g_object_unref (gc); -+ g_free (rects); -+} -+#endif -+ -+static void -+foo_scroll_area_scroll (FooScrollArea *area, -+ gint dx, -+ gint dy) -+{ -+ GdkRectangle allocation = GTK_WIDGET (area)->allocation; -+ GdkRectangle src_area; -+ GdkRectangle move_area; -+ GdkRegion *invalid_region; -+ -+ allocation.x = 0; -+ allocation.y = 0; -+ -+ src_area = allocation; -+ src_area.x -= dx; -+ src_area.y -= dy; -+ -+ invalid_region = gdk_region_rectangle (&allocation); -+ -+ if (gdk_rectangle_intersect (&allocation, &src_area, &move_area)) -+ { -+ GdkRegion *move_region; -+ -+#if 0 -+ g_print ("scrolling %d %d %d %d (%d %d)\n", -+ move_area.x, move_area.y, -+ move_area.width, move_area.height, -+ dx, dy); -+#endif -+ -+ simple_draw_drawable (area->priv->pixmap, area->priv->pixmap, -+ move_area.x, move_area.y, -+ move_area.x + dx, move_area.y + dy, -+ move_area.width, move_area.height); -+ gtk_widget_queue_draw (GTK_WIDGET (area)); -+ -+ move_region = gdk_region_rectangle (&move_area); -+ gdk_region_offset (move_region, dx, dy); -+ gdk_region_subtract (invalid_region, move_region); -+ gdk_region_destroy (move_region); -+ } -+ -+#if 0 -+ paint_region (area, invalid_region); -+#endif -+ -+ allocation_to_canvas_region (area, invalid_region); -+ -+ foo_scroll_area_invalidate_region (area, invalid_region); -+ -+ gdk_region_destroy (invalid_region); -+} -+ -+static void -+foo_scrollbar_adjustment_changed (GtkAdjustment *adj, -+ FooScrollArea *scroll_area) -+{ -+ GtkWidget *widget = GTK_WIDGET (scroll_area); -+ gint dx = 0; -+ gint dy = 0; -+ GdkRectangle old_viewport, new_viewport; -+ -+ get_viewport (scroll_area, &old_viewport); -+ -+ if (adj == scroll_area->priv->hadj) -+ { -+ /* FIXME: do we treat the offset as int or double, and, -+ * if int, how do we round? -+ */ -+ dx = (int)adj->value - scroll_area->priv->x_offset; -+ scroll_area->priv->x_offset = adj->value; -+ } -+ else if (adj == scroll_area->priv->vadj) -+ { -+ dy = (int)adj->value - scroll_area->priv->y_offset; -+ scroll_area->priv->y_offset = adj->value; -+ } -+ else -+ { -+ g_assert_not_reached (); -+ } -+ -+ if (GTK_WIDGET_REALIZED (widget)) -+ { -+ foo_scroll_area_scroll (scroll_area, -dx, -dy); -+ -+#if 0 -+ window_scroll_area (widget->window, &widget->allocation, -dx, -dy); -+#endif -+ translate_input_regions (scroll_area, -dx, -dy); -+ -+#if 0 -+ gdk_window_process_updates (widget->window, TRUE); -+#endif -+ } -+ -+ get_viewport (scroll_area, &new_viewport); -+ -+ emit_viewport_changed (scroll_area, &new_viewport, &old_viewport); -+} -+ -+static void -+set_one_adjustment (FooScrollArea *scroll_area, -+ GtkAdjustment *adjustment, -+ GtkAdjustment **location) -+{ -+ g_return_if_fail (location != NULL); -+ -+ if (adjustment == *location) -+ return; -+ -+ if (!adjustment) -+ adjustment = new_adjustment (); -+ -+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); -+ -+ if (*location) -+ { -+ g_signal_handlers_disconnect_by_func ( -+ *location, foo_scrollbar_adjustment_changed, scroll_area); -+ -+ g_object_unref (*location); -+ } -+ -+ *location = adjustment; -+ -+ g_object_ref_sink (*location); -+ -+ g_signal_connect (*location, "value_changed", -+ G_CALLBACK (foo_scrollbar_adjustment_changed), -+ scroll_area); -+} -+ -+static void -+foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area, -+ GtkAdjustment *hadjustment, -+ GtkAdjustment *vadjustment) -+{ -+ set_one_adjustment (scroll_area, hadjustment, &scroll_area->priv->hadj); -+ set_one_adjustment (scroll_area, vadjustment, &scroll_area->priv->vadj); -+ -+ set_adjustment_values (scroll_area); -+} -+ -+FooScrollArea * -+foo_scroll_area_new (void) -+{ -+ return g_object_new (FOO_TYPE_SCROLL_AREA, NULL); -+} -+ -+void -+foo_scroll_area_set_min_size (FooScrollArea *scroll_area, -+ int min_width, -+ int min_height) -+{ -+ scroll_area->priv->min_width = min_width; -+ scroll_area->priv->min_height = min_height; -+ -+ /* FIXME: think through invalidation. -+ * -+ * Goals: - no repainting everything on size_allocate(), -+ * - make sure input boxes are invalidated when -+ * needed -+ */ -+ gtk_widget_queue_resize (GTK_WIDGET (scroll_area)); -+} -+ -+#if 0 -+static void -+warn_about_adding_input_outside_expose (const char *func) -+{ -+ static gboolean warned = FALSE; -+ -+ if (!warned) -+ { -+ g_warning ("%s() can only be called " -+ "from the paint handler for the FooScrollArea\n", func); -+ -+ warned = TRUE; -+ } -+} -+#endif -+ -+static void -+user_to_device (double *x, double *y, -+ gpointer data) -+{ -+ cairo_t *cr = data; -+ -+ cairo_user_to_device (cr, x, y); -+} -+ -+static InputPath * -+make_path (FooScrollArea *area, -+ cairo_t *cr, -+ gboolean is_stroke, -+ FooScrollAreaEventFunc func, -+ gpointer data) -+{ -+ InputPath *path = g_new0 (InputPath, 1); -+ -+ path->is_stroke = is_stroke; -+ path->fill_rule = cairo_get_fill_rule (cr); -+ path->line_width = cairo_get_line_width (cr); -+ path->path = cairo_copy_path (cr); -+ path_foreach_point (path->path, user_to_device, cr); -+ path->func = func; -+ path->data = data; -+ path->next = area->priv->current_input->paths; -+ area->priv->current_input->paths = path; -+ return path; -+} -+ -+/* FIXME: we probably really want a -+ * -+ * foo_scroll_area_add_input_from_fill (area, cr, ...); -+ * and -+ * foo_scroll_area_add_input_from_stroke (area, cr, ...); -+ * as well. -+ */ -+void -+foo_scroll_area_add_input_from_fill (FooScrollArea *scroll_area, -+ cairo_t *cr, -+ FooScrollAreaEventFunc func, -+ gpointer data) -+{ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); -+ g_return_if_fail (cr != NULL); -+ g_return_if_fail (scroll_area->priv->current_input); -+ -+ make_path (scroll_area, cr, FALSE, func, data); -+} -+ -+void -+foo_scroll_area_add_input_from_stroke (FooScrollArea *scroll_area, -+ cairo_t *cr, -+ FooScrollAreaEventFunc func, -+ gpointer data) -+{ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); -+ g_return_if_fail (cr != NULL); -+ g_return_if_fail (scroll_area->priv->current_input); -+ -+ make_path (scroll_area, cr, TRUE, func, data); -+} -+ -+void -+foo_scroll_area_invalidate (FooScrollArea *scroll_area) -+{ -+ GtkWidget *widget = GTK_WIDGET (scroll_area); -+ -+ foo_scroll_area_invalidate_rect (scroll_area, -+ scroll_area->priv->x_offset, scroll_area->priv->y_offset, -+ widget->allocation.width, -+ widget->allocation.height); -+} -+ -+static void -+canvas_to_window (FooScrollArea *area, -+ GdkRegion *region) -+{ -+ GtkWidget *widget = GTK_WIDGET (area); -+ -+ gdk_region_offset (region, -+ -area->priv->x_offset + widget->allocation.x, -+ -area->priv->y_offset + widget->allocation.y); -+} -+ -+static void -+window_to_canvas (FooScrollArea *area, -+ GdkRegion *region) -+{ -+ GtkWidget *widget = GTK_WIDGET (area); -+ -+ gdk_region_offset (region, -+ area->priv->x_offset - widget->allocation.x, -+ area->priv->y_offset - widget->allocation.y); -+} -+ -+void -+foo_scroll_area_invalidate_region (FooScrollArea *area, -+ GdkRegion *region) -+{ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (area)); -+ -+ gdk_region_union (area->priv->update_region, region); -+ -+ if (GTK_WIDGET_REALIZED (area)) -+ { -+ canvas_to_window (area, region); -+ -+ gdk_window_invalidate_region (GTK_WIDGET (area)->window, region, TRUE); -+ -+ window_to_canvas (area, region); -+ } -+} -+ -+void -+foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area, -+ int x, -+ int y, -+ int width, -+ int height) -+{ -+ GdkRectangle rect = { x, y, width, height }; -+ GdkRegion *region; -+ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); -+ -+ region = gdk_region_rectangle (&rect); -+ -+ foo_scroll_area_invalidate_region (scroll_area, region); -+ -+ gdk_region_destroy (region); -+} -+ -+void -+foo_scroll_area_begin_grab (FooScrollArea *scroll_area, -+ FooScrollAreaEventFunc func, -+ gpointer input_data) -+{ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); -+ g_return_if_fail (!scroll_area->priv->grabbed); -+ -+ scroll_area->priv->grabbed = TRUE; -+ scroll_area->priv->grab_func = func; -+ scroll_area->priv->grab_data = input_data; -+ -+ /* FIXME: we should probably take a server grab */ -+ /* Also, maybe there should be support for setting the grab cursor */ -+} -+ -+void -+foo_scroll_area_end_grab (FooScrollArea *scroll_area) -+{ -+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); -+ -+ scroll_area->priv->grabbed = FALSE; -+ scroll_area->priv->grab_func = NULL; -+ scroll_area->priv->grab_data = NULL; -+} -+ -+gboolean -+foo_scroll_area_is_grabbed (FooScrollArea *scroll_area) -+{ -+ return scroll_area->priv->grabbed; -+} -+ -+void -+foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area, -+ int x, -+ int y) -+{ -+ int x_changed = scroll_area->priv->hadj->value != (double)x; -+ int y_changed = scroll_area->priv->vadj->value != (double)y; -+ -+ scroll_area->priv->hadj->value = x; -+ scroll_area->priv->vadj->value = y; -+ -+ set_adjustment_values (scroll_area); -+ -+ if (x_changed) -+ gtk_adjustment_value_changed (scroll_area->priv->hadj); -+ -+ if (y_changed) -+ gtk_adjustment_value_changed (scroll_area->priv->vadj); -+} -+ -+static gboolean -+rect_contains (const GdkRectangle *rect, int x, int y) -+{ -+ return (x >= rect->x && -+ y >= rect->y && -+ x < rect->x + rect->width && -+ y < rect->y + rect->height); -+} -+ -+static void -+stop_scrolling (FooScrollArea *area) -+{ -+#if 0 -+ g_print ("stop scrolling\n"); -+#endif -+ if (area->priv->auto_scroll_info) -+ { -+ g_source_remove (area->priv->auto_scroll_info->timeout_id); -+ g_timer_destroy (area->priv->auto_scroll_info->timer); -+ g_free (area->priv->auto_scroll_info); -+ -+ area->priv->auto_scroll_info = NULL; -+ } -+} -+ -+static gboolean -+scroll_idle (gpointer data) -+{ -+ GdkRectangle viewport, new_viewport; -+ FooScrollArea *area = data; -+ AutoScrollInfo *info = area->priv->auto_scroll_info; -+#if 0 -+ int dx, dy; -+#endif -+ int new_x, new_y; -+ double elapsed; -+ -+ get_viewport (area, &viewport); -+ -+#if 0 -+ g_print ("old info: %d %d\n", info->dx, info->dy); -+ -+ g_print ("timeout (%d %d)\n", dx, dy); -+#endif -+ -+#if 0 -+ viewport.x += info->dx; -+ viewport.y += info->dy; -+#endif -+ -+#if 0 -+ g_print ("new info %d %d\n", info->dx, info->dy); -+#endif -+ -+ elapsed = g_timer_elapsed (info->timer, NULL); -+ -+ info->res_x = elapsed * info->dx / 0.2; -+ info->res_y = elapsed * info->dy / 0.2; -+ -+#if 0 -+ g_print ("%f %f\n", info->res_x, info->res_y); -+#endif -+ -+ new_x = viewport.x + info->res_x; -+ new_y = viewport.y + info->res_y; -+ -+#if 0 -+ g_print ("%f\n", elapsed * (info->dx / 0.2)); -+#endif -+ -+#if 0 -+ g_print ("new_x, new_y\n: %d %d\n", new_x, new_y); -+#endif -+ -+ foo_scroll_area_set_viewport_pos (area, new_x, new_y); -+#if 0 -+ viewport.x + info->dx, -+ viewport.y + info->dy); -+#endif -+ -+ get_viewport (area, &new_viewport); -+ -+ if (viewport.x == new_viewport.x && -+ viewport.y == new_viewport.y && -+ (info->res_x > 1.0 || -+ info->res_y > 1.0 || -+ info->res_x < -1.0 || -+ info->res_y < -1.0)) -+ { -+ stop_scrolling (area); -+ -+ /* stop scrolling if it didn't have an effect */ -+ return FALSE; -+ } -+ -+ return TRUE; -+} -+ -+static void -+ensure_scrolling (FooScrollArea *area, -+ int dx, -+ int dy) -+{ -+ if (!area->priv->auto_scroll_info) -+ { -+#if 0 -+ g_print ("start scrolling\n"); -+#endif -+ area->priv->auto_scroll_info = g_new0 (AutoScrollInfo, 1); -+ area->priv->auto_scroll_info->timeout_id = -+ g_idle_add (scroll_idle, area); -+ area->priv->auto_scroll_info->timer = g_timer_new (); -+ } -+ -+#if 0 -+ g_print ("setting scrolling to %d %d\n", dx, dy); -+#endif -+ -+#if 0 -+ g_print ("dx, dy: %d %d\n", dx, dy); -+#endif -+ -+ area->priv->auto_scroll_info->dx = dx; -+ area->priv->auto_scroll_info->dy = dy; -+} -+ -+void -+foo_scroll_area_auto_scroll (FooScrollArea *scroll_area, -+ FooScrollAreaEvent *event) -+{ -+ GdkRectangle viewport; -+ -+ get_viewport (scroll_area, &viewport); -+ -+ if (rect_contains (&viewport, event->x, event->y)) -+ { -+ stop_scrolling (scroll_area); -+ } -+ else -+ { -+ int dx, dy; -+ -+ dx = dy = 0; -+ -+ if (event->y < viewport.y) -+ { -+ dy = event->y - viewport.y; -+ dy = MIN (dy + 2, 0); -+ } -+ else if (event->y >= viewport.y + viewport.height) -+ { -+ dy = event->y - (viewport.y + viewport.height - 1); -+ dy = MAX (dy - 2, 0); -+ } -+ -+ if (event->x < viewport.x) -+ { -+ dx = event->x - viewport.x; -+ dx = MIN (dx + 2, 0); -+ } -+ else if (event->x >= viewport.x + viewport.width) -+ { -+ dx = event->x - (viewport.x + viewport.width - 1); -+ dx = MAX (dx - 2, 0); -+ } -+ -+#if 0 -+ g_print ("dx, dy: %d %d\n", dx, dy); -+#endif -+ -+ ensure_scrolling (scroll_area, dx, dy); -+ } -+} -+ -+void -+foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area) -+{ -+ /* noop for now */ -+} -+ -+void -+foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area) -+{ -+ stop_scrolling (scroll_area); -+} -+ -+ -+ -+#if 0 -+/* -+ * Backing Store -+ */ -+struct BackingStore -+{ -+ GdkPixmap *pixmap; -+ GdkRegion *update_region; -+ int width; -+ int height; -+}; -+ -+static BackingStore * -+backing_store_new (GdkWindow *window, -+ int width, int height) -+{ -+ BackingStore *store = g_new0 (BackingStore, 1); -+ GdkRectangle rect = { 0, 0, width, height }; -+ -+ store->pixmap = gdk_pixmap_new (window, width, height, -1); -+ store->update_region = gdk_region_rectangle (&rect); -+ store->width = width; -+ store->height = height; -+ -+ return store; -+} -+ -+static void -+backing_store_free (BackingStore *store) -+{ -+ g_object_unref (store->pixmap); -+ gdk_region_destroy (store->update_region); -+ g_free (store); -+} -+ -+static void -+backing_store_draw (BackingStore *store, -+ GdkDrawable *dest, -+ GdkRegion *clip, -+ int x, -+ int y) -+{ -+ GdkGC *gc = gdk_gc_new (dest); -+ -+ gdk_gc_set_clip_region (gc, clip); -+ -+ gdk_draw_drawable (dest, gc, store->pixmap, -+ 0, 0, x, y, store->width, store->height); -+ -+ g_object_unref (gc); -+} -+ -+static void -+backing_store_scroll (BackingStore *store, -+ int dx, -+ int dy) -+{ -+ GdkGC *gc = gdk_gc_new (store->pixmap); -+ GdkRectangle rect; -+ -+ gdk_draw_drawable (store->pixmap, gc, store->pixmap, -+ 0, 0, dx, dy, -+ store->width, store->height); -+ -+ /* Invalidate vertically */ -+ rect.x = 0; -+ rect.width = store->width; -+ -+ if (dy > 0) -+ { -+ rect.y = 0; -+ rect.height = dy; -+ } -+ else -+ { -+ rect.y = store->height + dy; -+ rect.y = -dy; -+ } -+ -+ gdk_region_union_with_rect (store->update_region, &rect); -+ -+ /* Invalidate horizontally */ -+ rect.y = 0; -+ rect.height = store->height; -+ -+ if (dx > 0) -+ { -+ rect.x = 0; -+ rect.width = dx; -+ } -+ else -+ { -+ rect.x = store->width + dx; -+ rect.width = -dx; -+ } -+ -+ gdk_region_union_with_rect (store->update_region, &rect); -+} -+ -+static void -+backing_store_invalidate_rect (BackingStore *store, -+ GdkRectangle *rect) -+{ -+ gdk_region_union_with_rect (store->update_region, rect); -+} -+ -+static void -+backing_store_invalidate_region (BackingStore *store, -+ GdkRegion *region) -+{ -+ gdk_region_union (store->update_region, region); -+} -+ -+static void -+backing_store_invalidate_all (BackingStore *store) -+{ -+ GdkRectangle rect = { 0, 0, store->width, store->height }; -+ gdk_region_destroy (store->update_region); -+ store->update_region = gdk_region_rectangle (&rect); -+} -+ -+static void -+backing_store_resize (BackingStore *store, -+ int width, -+ int height) -+{ -+ GdkPixmap *pixmap = gdk_pixmap_new (store->pixmap, width, height, -1); -+ -+ /* Unfortunately we don't know in which direction we were resized, -+ * so we just assume we were dragged from the south-east corner. -+ * -+ * Although, maybe we could get the root coordinates of the input-window? -+ * That might just work, actually. We need to make sure metacity uses -+ * static gravity for the window before this will be useful. -+ */ -+ simple_draw_drawable (pixmap, store->pixmap, 0, 0, 0, 0, -1, -1); -+ -+ g_object_unref (store->pixmap); -+ -+ store->pixmap = pixmap; -+ -+ /* FIXME: invalidate uncovered strip only */ -+ -+ backing_store_invalidate_all (store); -+} -+ -+static void -+cclip_to_region (cairo_t *cr, GdkRegion *region) -+{ -+ int n_rects; -+ GdkRectangle *rects; -+ -+ gdk_region_get_rectangles (region, &rects, &n_rects); -+ -+ cairo_new_path (cr); -+ while (n_rects--) -+ { -+ GdkRectangle *rect = &(rects[n_rects]); -+ -+ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height); -+ } -+ cairo_clip (cr); -+ -+ g_free (rects); -+} -+ -+static void -+backing_store_process_updates (BackingStore *store, -+ ExposeFunc func, -+ gpointer data) -+{ -+ cairo_t *cr = gdk_cairo_create (store->pixmap); -+ GdkRegion *region = store->update_region; -+ store->update_region = gdk_region_new (); -+ -+ cclip_to_region (cr, store->update_region); -+ -+ func (cr, store->update_region, data); -+ -+ gdk_region_destroy (region); -+ cairo_destroy (cr); -+} -+ -+#endif -diff --git a/capplets/display/scrollarea.h b/capplets/display/scrollarea.h -new file mode 100644 -index 0000000..f043aab ---- /dev/null -+++ b/capplets/display/scrollarea.h -@@ -0,0 +1,124 @@ -+/* Copyright 2006, 2007, 2008, Soren Sandmann -+ * -+ * 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 of the License, or (at your option) any later version. -+ * -+ * This library 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 -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, -+ * Boston, MA 02111-1307, USA. -+ */ -+#include -+#include -+ -+#define FOO_TYPE_SCROLL_AREA (foo_scroll_area_get_type ()) -+#define FOO_SCROLL_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_SCROLL_AREA, FooScrollArea)) -+#define FOO_SCROLL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_SCROLL_AREA, FooScrollAreaClass)) -+#define FOO_IS_SCROLL_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_SCROLL_AREA)) -+#define FOO_IS_SCROLL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_SCROLL_AREA)) -+#define FOO_SCROLL_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_SCROLL_AREA, FooScrollAreaClass)) -+ -+typedef struct FooScrollArea FooScrollArea; -+typedef struct FooScrollAreaClass FooScrollAreaClass; -+typedef struct FooScrollAreaPrivate FooScrollAreaPrivate; -+typedef struct FooScrollAreaEvent FooScrollAreaEvent; -+ -+typedef enum -+{ -+ FOO_BUTTON_PRESS, -+ FOO_BUTTON_RELEASE, -+ FOO_MOTION -+} FooScrollAreaEventType; -+ -+struct FooScrollAreaEvent -+{ -+ FooScrollAreaEventType type; -+ int x; -+ int y; -+}; -+ -+typedef void (* FooScrollAreaEventFunc) (FooScrollArea *area, -+ FooScrollAreaEvent *event, -+ gpointer data); -+ -+struct FooScrollArea -+{ -+ GtkContainer parent_instance; -+ -+ FooScrollAreaPrivate *priv; -+}; -+ -+struct FooScrollAreaClass -+{ -+ GtkContainerClass parent_class; -+ -+ void (*set_scroll_adjustments) (FooScrollArea *scroll_area, -+ GtkAdjustment *hadjustment, -+ GtkAdjustment *vadjustment); -+ -+ void (*viewport_changed) (FooScrollArea *scroll_area, -+ GdkRectangle *old_viewport, -+ GdkRectangle *new_viewport); -+ -+ void (*paint) (FooScrollArea *scroll_area, -+ cairo_t *cr, -+ GdkRectangle *extents, -+ GdkRegion *region); -+}; -+ -+GType foo_scroll_area_get_type (void); -+ -+FooScrollArea *foo_scroll_area_new (void); -+ -+/* Set the requisition for the widget. */ -+void foo_scroll_area_set_min_size (FooScrollArea *scroll_area, -+ int min_width, -+ int min_height); -+ -+/* Set how much of the canvas can be scrolled into view */ -+void foo_scroll_area_set_size (FooScrollArea *scroll_area, -+ int width, -+ int height); -+void foo_scroll_area_set_size_fixed_y (FooScrollArea *scroll_area, -+ int width, -+ int height, -+ int old_y, -+ int new_y); -+void foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area, -+ int x, -+ int y); -+void foo_scroll_area_get_viewport (FooScrollArea *scroll_area, -+ GdkRectangle *viewport); -+void foo_scroll_area_add_input_from_stroke (FooScrollArea *scroll_area, -+ cairo_t *cr, -+ FooScrollAreaEventFunc func, -+ gpointer data); -+void foo_scroll_area_add_input_from_fill (FooScrollArea *scroll_area, -+ cairo_t *cr, -+ FooScrollAreaEventFunc func, -+ gpointer data); -+void foo_scroll_area_invalidate_region (FooScrollArea *area, -+ GdkRegion *region); -+void foo_scroll_area_invalidate (FooScrollArea *scroll_area); -+void foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area, -+ int x, -+ int y, -+ int width, -+ int height); -+void foo_scroll_area_begin_grab (FooScrollArea *scroll_area, -+ FooScrollAreaEventFunc func, -+ gpointer input_data); -+void foo_scroll_area_end_grab (FooScrollArea *scroll_area); -+gboolean foo_scroll_area_is_grabbed (FooScrollArea *scroll_area); -+ -+void foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area); -+void foo_scroll_area_auto_scroll (FooScrollArea *scroll_area, -+ FooScrollAreaEvent *event); -+void foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area); -diff --git a/capplets/display/xrandr-capplet.c b/capplets/display/xrandr-capplet.c -new file mode 100644 -index 0000000..c003b2d ---- /dev/null -+++ b/capplets/display/xrandr-capplet.c -@@ -0,0 +1,1676 @@ -+/* Monitor Settings. A preference panel for configuring monitors -+ * -+ * Copyright (C) 2007, 2008 Red Hat, Inc. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ * Author: Soren Sandmann -+ */ -+ -+#include -+#include -+#include -+#include -+#include "scrollarea.h" -+#define GNOME_DESKTOP_USE_UNSTABLE_API -+#include -+#include -+#include -+#include -+ -+typedef struct App App; -+typedef struct GrabInfo GrabInfo; -+ -+struct App -+{ -+ RWScreen *screen; -+ Configuration *current_configuration; -+ Output *current_output; -+ -+ GtkWidget *dialog; -+ GtkListStore *resolution_store; -+ GtkWidget *resolution_combo; -+ GtkWidget *refresh_combo; -+ GtkWidget *rotation_combo; -+ GtkWidget *panel_checkbox; -+ GtkWidget *panel_label; -+ GtkWidget *clone_checkbox; -+ -+ GtkWidget *area; -+ gboolean ignore_gui_changes; -+}; -+ -+static void rebuild_gui (App *app); -+static void on_rate_changed (GtkComboBox *box, gpointer data); -+ -+#if 0 -+static void -+show_error (const GError *err) -+{ -+ if (!err) -+ return; -+ -+ GtkWidget *dialog = gtk_message_dialog_new ( -+ NULL, -+ GTK_DIALOG_DESTROY_WITH_PARENT, -+ GTK_MESSAGE_WARNING, -+ GTK_BUTTONS_OK, err->message); -+ -+ gtk_window_set_title (GTK_WINDOW (dialog), ""); -+ -+ gtk_dialog_run (GTK_DIALOG (dialog)); -+ gtk_widget_destroy (dialog); -+} -+#endif -+ -+static gboolean -+do_free (gpointer data) -+{ -+ g_free (data); -+ return FALSE; -+} -+ -+static gchar * -+idle_free (gchar *s) -+{ -+ g_idle_add (do_free, s); -+ -+ return s; -+} -+ -+static int -+compare_outputs (const void *p1, const void *p2) -+{ -+ Output *const *o1 = p1; -+ Output *const *o2 = p2; -+ -+ return (**o1).x - (**o2).x; -+} -+ -+static void -+on_screen_changed (RWScreen *scr, -+ gpointer data) -+{ -+ Configuration *current; -+ App *app = data; -+ int i; -+ Output *best; -+ -+ current = configuration_new_current (app->screen); -+ -+ if (app->current_configuration) -+ configuration_free (app->current_configuration); -+ -+ app->current_configuration = current; -+ -+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) -+ { -+ Output *o = app->current_configuration->outputs[i]; -+ -+ g_print (" output %s %s: %d %d %d %d\n", o->name, o->on? "on" : "off", o->x, o->y, o->width, o->height); -+ } -+ -+ g_print ("sorting\n"); -+ /* Sort outputs according to X coordinate */ -+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) -+ ; -+ -+ qsort (app->current_configuration->outputs, i, sizeof (Output *), -+ compare_outputs); -+ -+#if 0 -+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) -+ { -+ Output *o = app->current_configuration->outputs[i]; -+ -+ g_print (" output: %d %d %d %d\n", o->x, o->y, o->width, o->height); -+ } -+#endif -+ -+ /* Select an output */ -+ best = NULL; -+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) -+ { -+ Output *output = app->current_configuration->outputs[i]; -+ -+ if (output->connected) -+ { -+ char *cur_name = -+ app->current_output? app->current_output->name : NULL; -+ -+ if ((cur_name && strcmp (output->name, cur_name) == 0) || !best) -+ best = output; -+ } -+ } -+ -+ app->current_output = best; -+ -+ rebuild_gui (app); -+} -+ -+static void -+on_viewport_changed (FooScrollArea *scroll_area, -+ GdkRectangle *old_viewport, -+ GdkRectangle *new_viewport) -+{ -+ foo_scroll_area_set_size (scroll_area, -+ new_viewport->width, -+ new_viewport->height); -+ -+ foo_scroll_area_invalidate (scroll_area); -+} -+ -+static void -+layout_set_font (PangoLayout *layout, const char *font) -+{ -+ PangoFontDescription *desc = -+ pango_font_description_from_string (font); -+ -+ if (desc) -+ { -+ pango_layout_set_font_description (layout, desc); -+ -+ pango_font_description_free (desc); -+ } -+} -+ -+static void -+clear_combo (GtkWidget *widget) -+{ -+ GtkComboBox *box = GTK_COMBO_BOX (widget); -+ GtkTreeModel *model = gtk_combo_box_get_model (box); -+ GtkListStore *store = GTK_LIST_STORE (model); -+ -+ gtk_list_store_clear (store); -+} -+ -+typedef struct -+{ -+ const char *text; -+ gboolean found; -+ GtkTreeIter iter; -+} ForeachInfo; -+ -+static gboolean -+foreach (GtkTreeModel *model, -+ GtkTreePath *path, -+ GtkTreeIter *iter, -+ gpointer data) -+{ -+ ForeachInfo *info = data; -+ char *text = NULL; -+ -+ gtk_tree_model_get (model, iter, 0, &text, -1); -+ -+ g_assert (text != NULL); -+ -+ if (strcmp (info->text, text) == 0) -+ { -+ info->found = TRUE; -+ info->iter = *iter; -+ return TRUE; -+ } -+ -+ return FALSE; -+} -+ -+static void -+add_key (GtkWidget *widget, -+ const char *text, -+ int width, int height, int rate, -+ RWRotation rotation) -+{ -+ ForeachInfo info; -+ GtkComboBox *box = GTK_COMBO_BOX (widget); -+ GtkTreeModel *model = gtk_combo_box_get_model (box); -+ GtkListStore *store = GTK_LIST_STORE (model); -+ gboolean retval; -+ -+ info.text = text; -+ info.found = FALSE; -+ -+ gtk_tree_model_foreach (model, foreach, &info); -+ -+ if (!info.found) -+ { -+ GtkTreeIter iter; -+ gtk_list_store_append (store, &iter); -+ -+ gtk_list_store_set (store, &iter, -+ 0, text, -+ 1, width, -+ 2, height, -+ 3, rate, -+ 4, width * height, -+ 5, rotation, -+ -1); -+ -+ retval = TRUE; -+ } -+ else -+ { -+ retval = FALSE; -+ } -+} -+ -+static gboolean -+combo_select (GtkWidget *widget, const char *text) -+{ -+ GtkComboBox *box = GTK_COMBO_BOX (widget); -+ GtkTreeModel *model = gtk_combo_box_get_model (box); -+ ForeachInfo info; -+ -+ info.text = text; -+ info.found = FALSE; -+ -+ gtk_tree_model_foreach (model, foreach, &info); -+ -+ if (!info.found) -+ return FALSE; -+ -+ gtk_combo_box_set_active_iter (box, &info.iter); -+ return TRUE; -+} -+ -+static gboolean -+has_similar_mode (RWOutput *output, RWMode *mode) -+{ -+ int i; -+ RWMode **modes = rw_output_list_modes (output); -+ int width = rw_mode_get_width (mode); -+ int height = rw_mode_get_height (mode); -+ -+ for (i = 0; modes[i] != NULL; ++i) -+ { -+ RWMode *m = modes[i]; -+ -+ if (rw_mode_get_width (m) == width && -+ rw_mode_get_height (m) == height) -+ { -+ return TRUE; -+ } -+ } -+ -+ return FALSE; -+} -+ -+static RWMode ** -+list_clone_modes (Configuration *config, RWScreen *screen) -+{ -+ int i; -+ GPtrArray *result; -+ RWMode **modes; -+ -+ for (i = 0; config->outputs[i] != NULL; ++i) -+ { -+ if (config->outputs[i]->connected) -+ { -+ RWOutput *output = -+ rw_screen_get_output_by_name (screen, config->outputs[i]->name); -+ -+ modes = rw_output_list_modes (output); -+ } -+ } -+ -+ if (!modes) -+ return NULL; -+ -+ result = g_ptr_array_new (); -+ -+ for (i = 0; modes[i] != NULL; ++i) -+ { -+ gboolean valid = TRUE; -+ int j; -+ -+ for (j = 0; config->outputs[j] != NULL; ++j) -+ { -+ if (config->outputs[j]->connected) -+ { -+ RWOutput *output = rw_screen_get_output_by_name ( -+ screen, config->outputs[j]->name); -+ -+ if (!has_similar_mode (output, modes[i])) -+ { -+ valid = FALSE; -+ break; -+ } -+ } -+ } -+ -+ if (valid) -+ g_ptr_array_add (result, modes[i]); -+ } -+ -+ g_ptr_array_add (result, NULL); -+ -+ return (RWMode **)g_ptr_array_free (result, FALSE); -+} -+ -+static RWMode ** -+get_current_modes (App *app) -+{ -+ RWOutput *output; -+ -+ if (app->current_configuration->clone) -+ { -+ return list_clone_modes (app->current_configuration, app->screen); -+ } -+ else -+ { -+ if (!app->current_output) -+ return NULL; -+ -+ output = rw_screen_get_output_by_name ( -+ app->screen, app->current_output->name); -+ -+ if (!output) -+ return NULL; -+ -+ return rw_output_list_modes (output); -+ } -+} -+ -+static void -+rebuild_rotation_combo (App *app) -+{ -+ typedef struct -+ { -+ RWRotation rotation; -+ const char * name; -+ } RotationInfo; -+ static const RotationInfo rotations[] = { -+ { RW_ROTATION_0, "Normal" }, -+ { RW_ROTATION_90, "Left" }, -+ { RW_ROTATION_270, "Right" }, -+ { RW_ROTATION_180, "Upside Down" }, -+ }; -+ const char *selection; -+ RWRotation current; -+ int i; -+ -+ clear_combo (app->rotation_combo); -+ -+ gtk_widget_set_sensitive ( -+ app->rotation_combo, app->current_output && app->current_output->on); -+ -+ if (!app->current_output) -+ return; -+ -+ current = app->current_output->rotation; -+ -+ selection = NULL; -+ for (i = 0; i < G_N_ELEMENTS (rotations); ++i) -+ { -+ const RotationInfo *info = &(rotations[i]); -+ -+ app->current_output->rotation = info->rotation; -+ -+ if (configuration_applicable (app->current_configuration, app->screen)) -+ { -+ add_key (app->rotation_combo, info->name, 0, 0, 0, info->rotation); -+ -+ if (info->rotation == current) -+ selection = info->name; -+ } -+ } -+ -+ app->current_output->rotation = current; -+ -+ if (!(selection && combo_select (app->rotation_combo, selection))) -+ combo_select (app->rotation_combo, "Normal"); -+} -+ -+#define idle_free_printf(x) idle_free (g_strdup_printf (x)) -+ -+static void -+rebuild_rate_combo (App *app) -+{ -+ GHashTable *rates; -+ RWMode **modes; -+ int best; -+ int i; -+ -+ clear_combo (app->refresh_combo); -+ -+ gtk_widget_set_sensitive ( -+ app->refresh_combo, app->current_output && app->current_output->on); -+ -+ if (!(modes = get_current_modes (app))) -+ return; -+ -+ rates = g_hash_table_new_full ( -+ g_str_hash, g_str_equal, (GFreeFunc)g_free, NULL); -+ -+ best = -1; -+ for (i = 0; modes[i] != NULL; ++i) -+ { -+ RWMode *mode = modes[i]; -+ int width, height, rate; -+ -+ width = rw_mode_get_width (mode); -+ height = rw_mode_get_height (mode); -+ rate = rw_mode_get_freq (mode); -+ -+ if (width == app->current_output->width && -+ height == app->current_output->height) -+ { -+ add_key (app->refresh_combo, -+ idle_free (g_strdup_printf ("%d Hz", rate)), -+ 0, 0, rate, -1); -+ -+ if (rate > best) -+ best = rate; -+ } -+ } -+ -+ if (!combo_select (app->refresh_combo, idle_free (g_strdup_printf ("%d Hz", app->current_output->rate)))) -+ combo_select (app->refresh_combo, idle_free (g_strdup_printf ("%d Hz", best))); -+} -+ -+static int -+count_active_outputs (App *app) -+{ -+ int i, count = 0; -+ -+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) -+ { -+ Output *output = app->current_configuration->outputs[i]; -+ if (output->on) -+ count++; -+ } -+ -+ return count; -+} -+ -+static void -+rebuild_resolution_combo (App *app) -+{ -+ int i; -+ RWMode **modes; -+ int best_w, best_h; -+ const char *current; -+ -+ clear_combo (app->resolution_combo); -+ -+ if (!(modes = get_current_modes (app))) -+ return; -+ -+ best_w = 0; -+ best_h = 0; -+ for (i = 0; modes[i] != NULL; ++i) -+ { -+ int width, height; -+ -+ width = rw_mode_get_width (modes[i]); -+ height = rw_mode_get_height (modes[i]); -+ -+ add_key (app->resolution_combo, -+ idle_free (g_strdup_printf ("%d x %d", width, height)), -+ width, height, 0, -1); -+ -+ if (width * height > best_w * best_h) -+ { -+ best_w = width; -+ best_h = height; -+ } -+ } -+ -+ if (count_active_outputs (app) > 1 || !app->current_output->on) -+ add_key (app->resolution_combo, "Off", 0, 0, 0, 0); -+ -+ if (!app->current_output->on) -+ { -+ current = "Off"; -+ } -+ else -+ { -+ current = idle_free (g_strdup_printf ("%d x %d", -+ app->current_output->width, -+ app->current_output->height)); -+ } -+ -+ -+ if (!combo_select (app->resolution_combo, current)) -+ { -+ combo_select (app->resolution_combo, -+ idle_free ( -+ g_strdup_printf ("%d x %d", best_w, best_h))); -+ } -+} -+ -+static void -+rebuild_gui (App *app) -+{ -+ gboolean sensitive; -+ -+ /* We would break spectacularly if we recursed, so -+ * just assert if that happens -+ */ -+ g_assert (app->ignore_gui_changes == FALSE); -+ -+ app->ignore_gui_changes = TRUE; -+ -+ sensitive = app->current_output? TRUE : FALSE; -+ -+#if 0 -+ g_print ("rebuild gui, is on: %d\n", app->current_output->on); -+#endif -+ -+ rebuild_resolution_combo (app); -+ rebuild_rate_combo (app); -+ rebuild_rotation_combo (app); -+ -+ gtk_widget_set_sensitive (app->resolution_combo, sensitive); -+ -+#if 0 -+ g_print ("sensitive: %d, on: %d\n", sensitive, app->current_output->on); -+#endif -+ gtk_widget_set_sensitive (app->panel_checkbox, sensitive); -+ -+ app->ignore_gui_changes = FALSE; -+ -+ if (app->current_configuration && app->current_configuration->clone) -+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->clone_checkbox), TRUE); -+ else -+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->clone_checkbox), FALSE); -+} -+ -+static gboolean -+get_mode (GtkWidget *widget, int *width, int *height, int *freq, RWRotation *rot) -+{ -+ GtkTreeIter iter; -+ GtkTreeModel *model; -+ GtkComboBox *box = GTK_COMBO_BOX (widget); -+ int dummy; -+ -+ if (!gtk_combo_box_get_active_iter (box, &iter)) -+ return FALSE; -+ -+ if (!width) -+ width = &dummy; -+ -+ if (!height) -+ height = &dummy; -+ -+ if (!freq) -+ freq = &dummy; -+ -+ if (!rot) -+ rot = (RWRotation *)&dummy; -+ -+ model = gtk_combo_box_get_model (box); -+ gtk_tree_model_get (model, &iter, -+ 1, width, -+ 2, height, -+ 3, freq, -+ 5, rot, -+ -1); -+ -+ return TRUE; -+ -+} -+ -+static void -+on_rotation_changed (GtkComboBox *box, gpointer data) -+{ -+ App *app = data; -+ RWRotation rotation; -+ -+ if (!app->current_output) -+ return; -+ -+ if (get_mode (app->rotation_combo, NULL, NULL, NULL, &rotation)) -+ app->current_output->rotation = rotation; -+ -+ foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); -+} -+ -+static void -+on_rate_changed (GtkComboBox *box, gpointer data) -+{ -+ App *app = data; -+ int rate; -+ -+ if (!app->current_output) -+ return; -+ -+ if (get_mode (app->refresh_combo, NULL, NULL, &rate, NULL)) -+ app->current_output->rate = rate; -+ -+ foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); -+} -+ -+static void -+on_resolution_changed (GtkComboBox *box, gpointer data) -+{ -+ App *app = data; -+ int width; -+ int height; -+ -+ if (!app->current_output) -+ return; -+ -+ if (get_mode (app->resolution_combo, &width, &height, NULL, NULL)) -+ { -+ app->current_output->width = width; -+ app->current_output->height = height; -+ -+ if (width == 0 || height == 0) -+ app->current_output->on = FALSE; -+ else -+ app->current_output->on = TRUE; -+ } -+ -+#if 0 -+ if (app->current_configuration) -+ { -+ x = 0; -+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) -+ { -+ Output *output = app->current_configuration->outputs[i]; -+ -+ if (output->connected) -+ { -+ output->x = x; -+ -+ x += output->width; -+ } -+ } -+ } -+#endif -+ -+ rebuild_rate_combo (app); -+ rebuild_rotation_combo (app); -+ -+ foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); -+} -+ -+static void -+on_clone_changed (GtkWidget *box, gpointer data) -+{ -+ App *app = data; -+ -+ app->current_configuration->clone = -+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (app->clone_checkbox)); -+ -+ if (app->current_configuration->clone) -+ { -+ int i; -+ -+ for (i = 0; app->current_configuration->outputs[i]; ++i) -+ { -+ if (app->current_configuration->outputs[i]->connected) -+ { -+ app->current_output = app->current_configuration->outputs[i]; -+ break; -+ } -+ } -+ } -+ -+ rebuild_gui (app); -+} -+ -+static void -+get_geometry (Output *output, int *w, int *h) -+{ -+ if (output->on) -+ { -+ *h = output->height; -+ *w = output->width; -+ } -+ else -+ { -+ *h = output->pref_height; -+ *w = output->pref_width; -+ } -+} -+ -+#define SPACE 15 -+#define MARGIN 15 -+ -+static GList * -+list_connected_outputs (App *app, int *total_w, int *total_h) -+{ -+ int i, dummy; -+ GList *result = NULL; -+ -+ if (!total_w) -+ total_w = &dummy; -+ if (!total_h) -+ total_h = &dummy; -+ -+ *total_w = 0; -+ *total_h = 0; -+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) -+ { -+ Output *output = app->current_configuration->outputs[i]; -+ -+ if (output->connected) -+ { -+ int w, h; -+ -+ result = g_list_prepend (result, output); -+ -+ get_geometry (output, &w, &h); -+ -+ *total_w += w; -+ *total_h += h; -+ } -+ } -+ -+ return g_list_reverse (result); -+} -+ -+static double -+compute_scale (App *app) -+{ -+ GList *connected_outputs; -+ int available_w, available_h; -+ int total_w, total_h; -+ int n_monitors; -+ GdkRectangle viewport; -+ -+ foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area), &viewport); -+ -+ connected_outputs = list_connected_outputs (app, &total_w, &total_h); -+ -+ n_monitors = g_list_length (connected_outputs); -+ -+ available_w = viewport.width - 2 * MARGIN - (n_monitors - 1) * SPACE; -+ available_h = viewport.height - 2 * MARGIN - (n_monitors - 1) * SPACE; -+ -+ return MIN ((double)available_w / total_w, (double)available_h / total_h); -+} -+ -+typedef struct Edge -+{ -+ Output *output; -+ int x1, y1; -+ int x2, y2; -+} Edge; -+ -+typedef struct Snap -+{ -+ Edge *snapper; /* Edge that should be snapped */ -+ Edge *snappee; -+ int dy, dx; -+} Snap; -+ -+static void -+add_edge (Output *output, int x1, int y1, int x2, int y2, GArray *edges) -+{ -+ Edge e; -+ -+ e.x1 = x1; -+ e.x2 = x2; -+ e.y1 = y1; -+ e.y2 = y2; -+ e.output = output; -+ -+ g_array_append_val (edges, e); -+} -+ -+static void -+list_edges_for_output (Output *output, GArray *edges) -+{ -+ int x, y, w, h; -+ -+ x = output->x; -+ y = output->y; -+ get_geometry (output, &w, &h); -+ -+ /* Top, Bottom, Left, Right */ -+ add_edge (output, x, y, x + w, y, edges); -+ add_edge (output, x, y + h, x + w, y + h, edges); -+ add_edge (output, x, y, x, y + h, edges); -+ add_edge (output, x + w, y, x + w, y + h, edges); -+} -+ -+static void -+list_edges (Configuration *config, GArray *edges) -+{ -+ int i; -+ -+ for (i = 0; config->outputs[i]; ++i) -+ { -+ Output *output = config->outputs[i]; -+ -+ if (output->connected) -+ list_edges_for_output (output, edges); -+ } -+} -+ -+static gboolean -+overlap (int s1, int e1, int s2, int e2) -+{ -+ return (!(e1 < s2 || s1 >= e2)); -+} -+ -+static gboolean -+horizontal_overlap (Edge *snapper, Edge *snappee) -+{ -+ if (snapper->y1 != snapper->y2 || snappee->y1 != snappee->y2) -+ return FALSE; -+ -+ return overlap (snapper->x1, snapper->x2, snappee->x1, snappee->x2); -+} -+ -+static gboolean -+vertical_overlap (Edge *snapper, Edge *snappee) -+{ -+ if (snapper->x1 != snapper->x2 || snappee->x1 != snappee->x2) -+ return FALSE; -+ -+ return overlap (snapper->y1, snapper->y2, snappee->y1, snappee->y2); -+} -+ -+static void -+add_snap (GArray *snaps, Snap snap) -+{ -+ if (ABS (snap.dx) <= 200 || ABS (snap.dy) <= 200) -+ g_array_append_val (snaps, snap); -+} -+ -+static void -+add_edge_snaps (Edge *snapper, Edge *snappee, GArray *snaps) -+{ -+ Snap snap; -+ -+ snap.snapper = snapper; -+ snap.snappee = snappee; -+ -+ if (horizontal_overlap (snapper, snappee)) -+ { -+ snap.dx = 0; -+ snap.dy = snappee->y1 - snapper->y1; -+ -+ add_snap (snaps, snap); -+ } -+ else if (vertical_overlap (snapper, snappee)) -+ { -+ snap.dy = 0; -+ snap.dx = snappee->x1 - snapper->x1; -+ -+ add_snap (snaps, snap); -+ } -+ -+ /* Corner snaps */ -+ /* 1->1 */ -+ snap.dx = snappee->x1 - snapper->x1; -+ snap.dy = snappee->y1 - snapper->y1; -+ -+ add_snap (snaps, snap); -+ -+ /* 1->2 */ -+ snap.dx = snappee->x2 - snapper->x1; -+ snap.dy = snappee->y2 - snapper->y1; -+ -+ add_snap (snaps, snap); -+ -+ /* 2->2 */ -+ snap.dx = snappee->x2 - snapper->x2; -+ snap.dy = snappee->y2 - snapper->y2; -+ -+ add_snap (snaps, snap); -+ -+ /* 2->1 */ -+ snap.dx = snappee->x1 - snapper->x2; -+ snap.dy = snappee->y1 - snapper->y2; -+ -+ add_snap (snaps, snap); -+} -+ -+static void -+list_snaps (Output *output, GArray *edges, GArray *snaps) -+{ -+ int i; -+ -+ for (i = 0; i < edges->len; ++i) -+ { -+ Edge *output_edge = &(g_array_index (edges, Edge, i)); -+ -+ if (output_edge->output == output) -+ { -+ int j; -+ -+ for (j = 0; j < edges->len; ++j) -+ { -+ Edge *edge = &(g_array_index (edges, Edge, j)); -+ -+ if (edge->output != output) -+ add_edge_snaps (output_edge, edge, snaps); -+ } -+ } -+ } -+} -+ -+#if 0 -+static void -+print_edge (Edge *edge) -+{ -+ g_print ("(%d %d %d %d)", edge->x1, edge->y1, edge->x2, edge->y2); -+} -+#endif -+ -+static gboolean -+corner_on_edge (int x, int y, Edge *e) -+{ -+ if (x == e->x1 && x == e->x2 && y >= e->y1 && y <= e->y2) -+ return TRUE; -+ -+ if (y == e->y1 && y == e->y2 && x >= e->x1 && x <= e->x2) -+ return TRUE; -+ -+ return FALSE; -+} -+ -+static gboolean -+edges_align (Edge *e1, Edge *e2) -+{ -+ if (corner_on_edge (e1->x1, e1->y1, e2)) -+ return TRUE; -+ -+ if (corner_on_edge (e2->x1, e2->y1, e1)) -+ return TRUE; -+ -+ return FALSE; -+} -+ -+static gboolean -+output_is_aligned (Output *output, GArray *edges) -+{ -+ gboolean result = FALSE; -+ int i; -+ -+ for (i = 0; i < edges->len; ++i) -+ { -+ Edge *output_edge = &(g_array_index (edges, Edge, i)); -+ -+ if (output_edge->output == output) -+ { -+ int j; -+ -+ for (j = 0; j < edges->len; ++j) -+ { -+ Edge *edge = &(g_array_index (edges, Edge, j)); -+ -+ /* We are aligned if an output edge matches -+ * an edge of another output -+ */ -+ if (edge->output != output_edge->output) -+ { -+ if (edges_align (output_edge, edge)) -+ { -+ result = TRUE; -+ goto done; -+ } -+ } -+ } -+ } -+ } -+done: -+ -+ return result; -+} -+ -+static void -+get_output_rect (Output *output, GdkRectangle *rect) -+{ -+ int w, h; -+ -+ get_geometry (output, &w, &h); -+ -+ rect->width = w; -+ rect->height = h; -+ rect->x = output->x; -+ rect->y = output->y; -+} -+ -+static gboolean -+output_overlaps (Output *output, Configuration *config) -+{ -+ int i; -+ GdkRectangle output_rect; -+ -+ get_output_rect (output, &output_rect); -+ -+ for (i = 0; config->outputs[i]; ++i) -+ { -+ Output *other = config->outputs[i]; -+ -+ if (other != output && other->connected) -+ { -+ GdkRectangle other_rect; -+ -+ get_output_rect (other, &other_rect); -+ if (gdk_rectangle_intersect (&output_rect, &other_rect, NULL)) -+ return TRUE; -+ } -+ } -+ -+ return FALSE; -+} -+ -+static gboolean -+configuration_is_aligned (Configuration *config, GArray *edges) -+{ -+ int i; -+ gboolean result = TRUE; -+ -+ for (i = 0; config->outputs[i]; ++i) -+ { -+ Output *output = config->outputs[i]; -+ -+ if (output->connected) -+ { -+ if (!output_is_aligned (output, edges)) -+ return FALSE; -+ -+ if (output_overlaps (output, config)) -+ return FALSE; -+ } -+ } -+ -+ return result; -+} -+ -+struct GrabInfo -+{ -+ int grab_x; -+ int grab_y; -+ int output_x; -+ int output_y; -+}; -+ -+static gboolean -+is_corner_snap (const Snap *s) -+{ -+ return s->dx != 0 && s->dy != 0; -+} -+ -+static int -+compare_snaps (gconstpointer v1, gconstpointer v2) -+{ -+ const Snap *s1 = v1; -+ const Snap *s2 = v2; -+ int sv1 = MAX (ABS (s1->dx), ABS (s1->dy)); -+ int sv2 = MAX (ABS (s2->dx), ABS (s2->dy)); -+ int d; -+ -+ d = sv1 - sv2; -+ -+ /* This snapping algorithm is good for rock'n'roll, but -+ * this is probably a better: -+ * -+ * First do a horizontal/vertical snap, then -+ * with the new coordinates from that snap, -+ * do a corner snap. -+ * -+ * Right now, it's confusing that corner snapping -+ * depends on the distance in an axis that you can't actually see. -+ * -+ */ -+ if (d == 0) -+ { -+ if (is_corner_snap (s1) && !is_corner_snap (s2)) -+ return -1; -+ else if (is_corner_snap (s2) && !is_corner_snap (s1)) -+ return 1; -+ else -+ return 0; -+ } -+ else -+ { -+ return d; -+ } -+} -+ -+static void -+on_output_event (FooScrollArea *area, -+ FooScrollAreaEvent *event, -+ gpointer data) -+{ -+ Output *output = data; -+ App *app = g_object_get_data (G_OBJECT (area), "app"); -+ -+ if (event->type == FOO_BUTTON_PRESS) -+ { -+ GrabInfo *info; -+ -+ app->current_output = output; -+ -+ rebuild_gui (app); -+ -+ foo_scroll_area_begin_grab (area, on_output_event, data); -+ -+ info = g_new0 (GrabInfo, 1); -+ info->grab_x = event->x; -+ info->grab_y = event->y; -+ info->output_x = output->x; -+ info->output_y = output->y; -+ -+ output->user_data = info; -+ -+ foo_scroll_area_invalidate (area); -+ } -+ else -+ { -+ if (foo_scroll_area_is_grabbed (area)) -+ { -+ GrabInfo *info = output->user_data; -+ double scale = compute_scale (app); -+ int old_x, old_y; -+ int new_x, new_y; -+ int i; -+ GArray *edges, *snaps, *new_edges; -+ -+ old_x = output->x; -+ old_y = output->y; -+ new_x = info->output_x + (event->x - info->grab_x) / scale; -+ new_y = info->output_y + (event->y - info->grab_y) / scale; -+ -+ output->x = new_x; -+ output->y = new_y; -+ -+ edges = g_array_new (TRUE, TRUE, sizeof (Edge)); -+ snaps = g_array_new (TRUE, TRUE, sizeof (Snap)); -+ new_edges = g_array_new (TRUE, TRUE, sizeof (Edge)); -+ -+ list_edges (app->current_configuration, edges); -+ list_snaps (output, edges, snaps); -+ -+ g_array_sort (snaps, compare_snaps); -+ -+ output->x = info->output_x; -+ output->y = info->output_y; -+ -+ for (i = 0; i < snaps->len; ++i) -+ { -+ Snap *snap = &(g_array_index (snaps, Snap, i)); -+ GArray *new_edges = g_array_new (TRUE, TRUE, sizeof (Edge)); -+ -+ output->x = new_x + snap->dx; -+ output->y = new_y + snap->dy; -+ -+ g_array_set_size (new_edges, 0); -+ list_edges (app->current_configuration, new_edges); -+ -+ if (configuration_is_aligned (app->current_configuration, new_edges)) -+ { -+ g_array_free (new_edges, TRUE); -+ break; -+ } -+ else -+ { -+ output->x = info->output_x; -+ output->y = info->output_y; -+ } -+ } -+ -+ g_array_free (new_edges, TRUE); -+ g_array_free (snaps, TRUE); -+ g_array_free (edges, TRUE); -+ -+ if (event->type == FOO_BUTTON_RELEASE) -+ { -+ foo_scroll_area_end_grab (area); -+ -+ g_free (output->user_data); -+ output->user_data = NULL; -+ -+ g_print ("new position: %d %d %d %d\n", output->x, output->y, output->width, output->height); -+ } -+ -+ foo_scroll_area_invalidate (area); -+ } -+ } -+} -+ -+#if 0 -+static void -+on_canvas_event (FooScrollArea *area, -+ FooScrollAreaEvent *event, -+ gpointer data) -+{ -+ App *app = g_object_get_data (G_OBJECT (area), "app"); -+ -+ if (event->type == FOO_BUTTON_PRESS) -+ { -+ app->current_output = NULL; -+ -+ rebuild_gui (app); -+ -+ foo_scroll_area_invalidate (area); -+ } -+} -+#endif -+ -+static PangoLayout * -+get_display_name (App *app, -+ Output *output) -+{ -+ const char *text; -+ -+ if (app->current_configuration->clone) -+ text = "Cloned Output"; -+ else -+ text = output->display_name; -+ -+ return gtk_widget_create_pango_layout ( -+ GTK_WIDGET (app->area), text); -+} -+ -+#define BACKGROUND_FILL_RGBA 0.72, 0.78, 0.87, 1.0 -+#define BACKGROUND_STROKE_RGBA 0.44, 0.59, 0.76, 1.0 -+ -+static void -+paint_background (FooScrollArea *area, -+ cairo_t *cr) -+{ -+ GdkRectangle viewport; -+ -+ foo_scroll_area_get_viewport (area, &viewport); -+ -+ cairo_set_source_rgba (cr, BACKGROUND_FILL_RGBA); -+ -+ cairo_rectangle (cr, -+ viewport.x, viewport.y, -+ viewport.width, viewport.height); -+ -+ cairo_fill_preserve (cr); -+ -+#if 0 -+ foo_scroll_area_add_input_from_fill (area, cr, on_canvas_event, NULL); -+#endif -+ -+ cairo_set_source_rgba (cr, BACKGROUND_STROKE_RGBA); -+ -+ cairo_stroke (cr); -+} -+ -+static void -+paint_output (App *app, cairo_t *cr, int i) -+{ -+ int w, h; -+ double scale = compute_scale (app); -+ double x, y; -+ int total_w, total_h; -+ GList *connected_outputs = list_connected_outputs (app, &total_w, &total_h); -+ Output *output = g_list_nth (connected_outputs, i)->data; -+ PangoLayout *layout = get_display_name (app, output); -+ PangoRectangle extent; -+ GdkRectangle viewport; -+ -+ cairo_save (cr); -+ -+ foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area), &viewport); -+ -+ get_geometry (output, &w, &h); -+ -+#if 0 -+ g_print ("%s (%p) geometry %d %d %d\n", output->name, output, -+ w, h, output->rate); -+#endif -+ -+ viewport.height -= 2 * MARGIN; -+ viewport.width -= 2 * MARGIN; -+ -+ x = output->x * scale + MARGIN + (viewport.width - total_w * scale) / 2.0; -+ y = output->y * scale + MARGIN + (viewport.height - total_h * scale) / 2.0; -+ -+#if 0 -+ g_print ("scaled: %f %f\n", x, y); -+ -+ g_print ("scale: %f\n", scale); -+ -+ g_print ("%f %f %f %f\n", x, y, w * scale + 0.5, h * scale + 0.5); -+#endif -+ -+ cairo_rectangle (cr, x, y, w * scale + 0.5, h * scale + 0.5); -+ cairo_clip_preserve (cr); -+ -+ if (output->on) -+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); -+ else -+ cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); -+ -+ foo_scroll_area_add_input_from_fill (FOO_SCROLL_AREA (app->area), -+ cr, on_output_event, output); -+ cairo_fill (cr); -+ -+ if (output == app->current_output) -+ { -+ cairo_rectangle (cr, x + 2, y + 2, w * scale + 0.5 - 4, h * scale + 0.5 - 4); -+ -+ cairo_set_line_width (cr, 4); -+ cairo_set_source_rgba (cr, 0.33, 0.43, 0.57, 1.0); -+ cairo_stroke (cr); -+ } -+ -+ cairo_rectangle (cr, x + 0.5, y + 0.5, w * scale + 0.5 - 1, h * scale + 0.5 - 1); -+ -+ cairo_set_line_width (cr, 1); -+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); -+ -+ cairo_stroke (cr); -+ cairo_set_line_width (cr, 2); -+ -+ layout_set_font (layout, "Sans Bold 12"); -+ -+ pango_layout_get_pixel_extents (layout, NULL, &extent); -+ -+ extent.x = x + ((w * scale + 0.5) - extent.width) / 2; -+ extent.y = y + ((h * scale + 0.5) - extent.height) / 2; -+ -+ cairo_move_to (cr, extent.x, extent.y); -+ -+ if (output->on) -+ cairo_set_source_rgb (cr, 0.2, 0.2, 0.8); -+ else -+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); -+ -+ pango_cairo_show_layout (cr, layout); -+ g_object_unref (layout); -+ -+ cairo_restore (cr); -+} -+ -+static void -+on_area_paint (FooScrollArea *area, -+ cairo_t *cr, -+ GdkRectangle *extent, -+ GdkRegion *region, -+ gpointer data) -+{ -+ App *app = data; -+ double scale; -+ GList *connected_outputs = NULL; -+ GList *list; -+ -+ paint_background (area, cr); -+ -+ if (!app->current_configuration) -+ return; -+ -+ scale = compute_scale (app); -+ connected_outputs = list_connected_outputs (app, NULL, NULL); -+ -+#if 0 -+ g_print ("scale: %f\n", scale); -+#endif -+ -+ for (list = connected_outputs; list != NULL; list = list->next) -+ { -+ paint_output (app, cr, g_list_position (connected_outputs, list)); -+ -+ if (app->current_configuration->clone) -+ break; -+ } -+} -+ -+static void -+make_text_combo (GtkWidget *widget, int sort_column) -+{ -+ GtkComboBox *box = GTK_COMBO_BOX (widget); -+ GtkListStore *store = gtk_list_store_new ( -+ 6, -+ G_TYPE_STRING, /* Text */ -+ G_TYPE_INT, /* Width */ -+ G_TYPE_INT, /* Height */ -+ G_TYPE_INT, /* Frequency */ -+ G_TYPE_INT, /* Width * Height */ -+ G_TYPE_INT); /* Rotation */ -+ -+ GtkCellRenderer *cell; -+ -+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (widget)); -+ -+ gtk_combo_box_set_model (box, GTK_TREE_MODEL (store)); -+ -+ cell = gtk_cell_renderer_text_new (); -+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, TRUE); -+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell, -+ "text", 0, -+ NULL); -+ -+ if (sort_column != -1) -+ { -+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), -+ sort_column, -+ GTK_SORT_DESCENDING); -+ } -+} -+ -+static Atom -+gnome_randr_atom (void) -+{ -+ static Atom atom = None; -+ -+ if (!atom) -+ { -+ atom = XInternAtom (gdk_x11_get_default_xdisplay(), -+ "_GNOME_RANDR_ATOM", FALSE); -+ } -+ -+ return atom; -+} -+ -+static void -+compute_virtual_size_for_configuration (Configuration *config, int *ret_width, int *ret_height) -+{ -+ int i; -+ int width, height; -+ -+ width = height = 0; -+ -+ for (i = 0; config->outputs[i] != NULL; i++) -+ { -+ Output *output; -+ -+ output = config->outputs[i]; -+ -+ if (output->on) -+ { -+ width = MAX (width, output->x + output->width); -+ height = MAX (height, output->y + output->height); -+ } -+ } -+ -+ *ret_width = width; -+ *ret_height = height; -+} -+ -+static void -+check_required_virtual_size (App *app) -+{ -+ int req_width, req_height; -+ int min_width, max_width; -+ int min_height, max_height; -+ -+ compute_virtual_size_for_configuration (app->current_configuration, &req_width, &req_height); -+ -+ rw_screen_get_ranges (app->screen, &min_width, &max_width, &min_height, &max_height); -+ -+ g_print ("X Server supports:\n"); -+ g_print ("min_width = %d, max_width = %d\n", min_width, max_width); -+ g_print ("min_height = %d, max_height = %d\n", min_height, max_height); -+ -+ g_print ("Requesting size of %dx%d\n", req_width, req_height); -+ -+ if (!(min_width <= req_width && req_width <= max_width -+ && min_height <= req_height && req_height <= max_height)) -+ { -+ /* FIXME: present a useful dialog, maybe even before the user tries to Apply */ -+ g_print ("Your X server needs a larger Virtual size!\n"); -+ } -+} -+ -+static void -+apply (App *app) -+{ -+ GError *err = NULL; -+ -+ configuration_sanitize (app->current_configuration); -+ -+ check_required_virtual_size (app); -+ -+ foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); -+ -+ if (configuration_save (app->current_configuration, &err)) -+ { -+ XEvent message; -+ -+ message.xclient.type = ClientMessage; -+ message.xclient.message_type = gnome_randr_atom(); -+ message.xclient.format = 8; -+ -+ g_print ("Sending client message\n"); -+ -+ XSendEvent (gdk_x11_get_default_xdisplay(), -+ gdk_x11_get_default_root_xwindow(), -+ FALSE, -+ StructureNotifyMask, &message); -+ } -+} -+ -+static void -+on_detect_displays (GtkWidget *widget, gpointer data) -+{ -+ App *app = data; -+ -+ rw_screen_refresh (app->screen); -+} -+ -+static void -+run_application (App *app) -+{ -+#ifndef GLADEDIR -+#define GLADEDIR "." -+#endif -+#define GLADE_FILE GLADEDIR "/display-capplet.glade" -+ GladeXML *xml; -+ GtkWidget *align; -+ -+ xml = glade_xml_new (GLADE_FILE, NULL, NULL); -+ if (!xml) -+ { -+ g_warning ("Could not open " GLADE_FILE); -+ return; -+ } -+ -+ app->screen = rw_screen_new (gdk_screen_get_default(), -+ on_screen_changed, app); -+ -+ app->dialog = glade_xml_get_widget (xml, "dialog"); -+ -+ gtk_window_set_default_icon_name ("gnome-display-properties"); -+ gtk_window_set_icon_name (GTK_WINDOW (app->dialog), -+ "gnome-display-properties"); -+ -+ app->resolution_combo = glade_xml_get_widget (xml, "resolution_combo"); -+ g_signal_connect (app->resolution_combo, "changed", -+ G_CALLBACK (on_resolution_changed), app); -+ -+ app->refresh_combo = glade_xml_get_widget (xml, "refresh_combo"); -+ g_signal_connect (app->refresh_combo, "changed", -+ G_CALLBACK (on_rate_changed), app); -+ -+ app->rotation_combo = glade_xml_get_widget (xml, "rotation_combo"); -+ g_signal_connect (app->rotation_combo, "changed", -+ G_CALLBACK (on_rotation_changed), app); -+ -+ app->clone_checkbox = glade_xml_get_widget (xml, "clone_checkbox"); -+ g_signal_connect (app->clone_checkbox, "toggled", -+ G_CALLBACK (on_clone_changed), app); -+ -+ g_signal_connect (glade_xml_get_widget (xml, "detect_displays_button"), -+ "clicked", G_CALLBACK (on_detect_displays), app); -+ -+ app->panel_checkbox = glade_xml_get_widget (xml, "panel_checkbox"); -+ app->panel_label = glade_xml_get_widget (xml, "panel_label"); -+ -+ make_text_combo (app->resolution_combo, 4); -+ make_text_combo (app->refresh_combo, 3); -+ make_text_combo (app->rotation_combo, -1); -+ -+ g_assert (app->panel_checkbox); -+ -+ /* Scroll Area */ -+ app->area = (GtkWidget *)foo_scroll_area_new (); -+ -+ g_object_set_data (G_OBJECT (app->area), "app", app); -+ -+ /* FIXME: this should be computed dynamically */ -+ foo_scroll_area_set_min_size (FOO_SCROLL_AREA (app->area), -1, 200); -+ gtk_widget_show (app->area); -+ g_signal_connect (app->area, "paint", -+ G_CALLBACK (on_area_paint), app); -+ g_signal_connect (app->area, "viewport_changed", -+ G_CALLBACK (on_viewport_changed), app); -+ -+ align = glade_xml_get_widget (xml, "align"); -+ -+ gtk_container_add (GTK_CONTAINER (align), app->area); -+ -+ on_screen_changed (app->screen, app); -+ rebuild_gui (app); -+ -+ gtk_widget_hide (app->panel_checkbox); -+ gtk_widget_hide (app->panel_label); -+ -+restart: -+ switch (gtk_dialog_run (GTK_DIALOG (app->dialog))) -+ { -+ default: -+ g_print ("Unknown response\n"); -+ /* Fall Through */ -+ case GTK_RESPONSE_DELETE_EVENT: -+ case GTK_RESPONSE_CLOSE: -+ g_print ("Close\n"); -+ break; -+ -+ case GTK_RESPONSE_HELP: -+ g_print ("Help\n"); -+ goto restart; -+ break; -+ -+ case GTK_RESPONSE_APPLY: -+ apply (app); -+ goto restart; -+ break; -+ } -+ -+ gtk_widget_destroy (app->dialog); -+} -+ -+int -+main (int argc, char **argv) -+{ -+ App *app; -+ -+#if 0 -+ bindtextdomain (GETTEXT_PACKAGE, DESKTOPEFFECTSLOCALEDIR); -+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -+ textdomain (GETTEXT_PACKAGE); -+#endif -+ -+ gtk_init (&argc, &argv); -+ -+ app = g_new0 (App, 1); -+ -+ run_application (app); -+ -+ return 0; -+} diff --git a/gnome-control-center.changes b/gnome-control-center.changes index 8cbafef..53355f5 100644 --- a/gnome-control-center.changes +++ b/gnome-control-center.changes @@ -1,3 +1,13 @@ +------------------------------------------------------------------- +Tue Aug 5 18:36:46 CEST 2008 - rodrigo@suse.de + +- Update to version 2.23.6: + + Various fixes and improvements + + Add freedesktop sound theme support + + Updated translations + + Updated randr 1.2 code +- Removed upstreamed randr1.2 patch + ------------------------------------------------------------------- Fri Jun 20 17:49:37 CEST 2008 - maw@suse.de diff --git a/gnome-control-center.spec b/gnome-control-center.spec index f524230..1a5b03a 100644 --- a/gnome-control-center.spec +++ b/gnome-control-center.spec @@ -1,10 +1,17 @@ # -# spec file for package gnome-control-center (Version 2.23.4) +# spec file for package gnome-control-center (Version 2.23.6) # # Copyright (c) 2008 SUSE LINUX Products GmbH, Nuernberg, Germany. -# This file and all modifications and additions to the pristine -# package are under the same license as the package itself. # +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + # Please submit bugfixes or comments via http://bugs.opensuse.org/ # @@ -13,12 +20,12 @@ Name: gnome-control-center %define _name control-center -BuildRequires: desktop-file-utils docbook-xsl-stylesheets eel-devel fdupes gnome-common gnome-desktop-devel gnome-doc-utils-devel gnome-menus-devel gnome-panel-devel gnome-patch-translation gnome-settings-daemon-devel gstreamer010-plugins-base-devel intltool libglade2-devel libgnomekbd-devel libidl librsvg-devel libxklavier-devel metacity-devel nautilus-devel pam-devel perl-XML-Parser scrollkeeper sgml-skel update-desktop-files xorg-x11 +BuildRequires: desktop-file-utils docbook-xsl-stylesheets eel-devel evolution-data-server-devel fdupes gnome-common gnome-desktop-devel gnome-doc-utils-devel gnome-menus-devel gnome-panel-devel gnome-patch-translation gnome-settings-daemon-devel gstreamer010-plugins-base-devel intltool libglade2-devel libgnomekbd-devel libidl librsvg-devel libxklavier-devel metacity-devel nautilus-devel pam-devel perl-XML-Parser scrollkeeper sgml-skel update-desktop-files xorg-x11 License: GPL v2 or later Group: System/GUI/GNOME Obsoletes: fontilus themus acme Provides: fontilus themus acme -Version: 2.23.4 +Version: 2.23.6 Release: 1 Summary: The GNOME Control Center Source: gnome-%{_name}-%{version}.tar.bz2 @@ -42,8 +49,6 @@ Patch15: gnome-control-center-default-applications-ftp+news.patch Requires: gnome-icon-theme # PATCH-FEATURE-OPENSUSE gnome-control-center-use-preferences-menu.patch Patch28: gnome-control-center-use-preferences-menu.patch -# PATCH-FEATURE-UPSTREAM gnome-settings-daemon-1.2.diff -- Add support for RandR 1.2 to gnome-display-properties. federico@novell.com -Patch29: gnome-control-center-randr-1.2.diff # PATCH-FIX-OPENSUSE gnome-control-center-bnc373197-layout-switch-keybinding.patch bnc373197 vuntz@novell.com -- this is a temporary hack to change the default layout switching keybinding Patch30: gnome-control-center-bnc373197-layout-switch-keybinding.patch Url: http://www.gnome.org @@ -104,7 +109,6 @@ gnome-patch-translation-prepare %patch14 -p1 cp %{S:3} capplets/passwd/change-password.png %patch28 -%patch29 -p1 %patch30 -p1 gnome-patch-translation-update @@ -227,6 +231,13 @@ fi %{_libdir}/pkgconfig/*.pc %changelog +* Tue Aug 05 2008 rodrigo@suse.de +- Update to version 2.23.6: + + Various fixes and improvements + + Add freedesktop sound theme support + + Updated translations + + Updated randr 1.2 code +- Removed upstreamed randr1.2 patch * Fri Jun 20 2008 maw@suse.de - Update to version 2.23.4: + Various fixes and improvements