<html>
<body>
  <div id="body" style="background-color:#ffffff;" >
<table cellspacing="0" cellpadding="0" border="0" rules="cols">
<tr class="head" style="border-bottom-width:1px;border-bottom-style:solid;" ><td class="headtd" style="padding:0;padding-top:.2em;" colspan="4">Commit in <b><tt>lxdream</tt></b></td></tr>
<tr><td><tt>po/<a href="#file1">POTFILES.in</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+2</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-3</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>src/<a href="#file2">Makefile.am</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+5</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-5</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>   /<a href="#file3">Makefile.in</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+43</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-59</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>src/cocoaui/<a href="#file4"><span id="addedalt" style="background-color:#ccf7cc;" >cocoa_cfg.m</span></a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+383</td><td></td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" align="right" nowrap="nowrap">added d82e04e6d497</td></tr>
<tr><td><tt>           /<a href="#file5">cocoa_ctrl.m</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+11</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-287</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>           /<a href="#file6"><span id="removedalt" style="background-color:#f7cccc;" >cocoa_path.m</span></a></tt></td><td></td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-135</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e removed</td></tr>
<tr><td><tt>           /<a href="#file7">cocoa_prefs.m</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+51</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-10</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>           /<a href="#file8">cocoaui.h</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+19</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-3</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>src/<a href="#file9">config.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+116</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-90</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>   /<a href="#file10">config.h</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+32</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-8</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>   /<a href="#file11">display.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+34</td><td></td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>   /<a href="#file12">display.h</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+7</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-1</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>   /<a href="#file13">dreamcast.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+30</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-8</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>   /<a href="#file14">dreamcast.h</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+4</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-2</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>src/gtkui/<a href="#file15"><span id="added" style="background-color:#ddffdd;" >gtk_cfg.c</span></a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+330</td><td></td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" align="right" nowrap="nowrap">added d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>         /<a href="#file16">gtk_ctrl.c</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+9</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-169</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>         /<a href="#file17"><span id="removed" style="background-color:#ffdddd;" >gtk_hotkeys.c</span></a></tt></td><td></td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-187</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e removed</td></tr>
<tr class="alt" style=";" ><td><tt>         /<a href="#file18"><span id="removedalt" style="background-color:#f7cccc;" >gtk_path.c</span></a></tt></td><td></td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-130</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e removed</td></tr>
<tr><td><tt>         /<a href="#file19">gtkcb.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+3</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-3</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>         /<a href="#file20">gtkui.h</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+3</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-1</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>src/<a href="#file21">hotkeys.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+68</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-104</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>   /<a href="#file22">hotkeys.h</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+2</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-2</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>src/maple/<a href="#file23">controller.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+35</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-51</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>         /<a href="#file24">kbd.c</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+1</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-1</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>         /<a href="#file25">lightgun.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+35</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-49</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>         /<a href="#file26">maple.c</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+1</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-8</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>         /<a href="#file27">maple.h</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+6</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-6</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr class="alt" style=";" ><td><tt>         /<a href="#file28">mouse.c</a></tt></td><td id="addedalt" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ccf7cc;" align="right">+1</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-1</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td><tt>         /<a href="#file29">vmu.c</a></tt></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+24</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-19</td><td class="headtd2" style="padding-left:.3em;padding-right:.3em;" nowrap="nowrap">182cfe43c09e -> d82e04e6d497</td></tr>
<tr><td></td><td id="added" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ddffdd;" align="right">+1255</td><td id="removed" class="headtd2" style="padding-left:.3em;padding-right:.3em; background-color:#ffdddd;" align="right">-1342</td><td></td></tr>
</table>
<small id="info" style="color: #888888;" >2 added + 3 removed + 24 modified, total 29 files</small><br />
<div class="tasklist" style="padding:4px;border:1px dashed #000000;margin-top:1em;" ><ul>
<li><a href="#task1">TODO:</a></li>
</ul></div>
<pre class="comment" style="white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;white-space:pre-wrap;word-wrap:break-word;padding:4px;border:1px dashed #000000;background-color:#ffffdd;" >
Heavy configuration management refactor
  - Configuration groups now take both an on_change event handler and a
    default keybinding handler, making most keybinding tasks quite simple
  - GUI configuration all merged into a unified model, drastically reducing
    the amount of GUI config code.

Bonuses
  - OSX now has a hotkey preference pane
  - GTK keybinding editor is much more usable
</pre>
<hr /><a name="file1" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/po</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>POTFILES.in</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/po/POTFILES.in
+++ lxdream/po/POTFILES.in
@@ -13,9 +13,9 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > src/bootstrap.c
 src/bootstrap.h
 src/clock.h
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+src/cocoaui/cocoa_cfg.m
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > src/cocoaui/cocoa_ctrl.m
 src/cocoaui/cocoa_gd.m
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-src/cocoaui/cocoa_path.m
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > src/cocoaui/cocoa_prefs.m
 src/cocoaui/cocoaui.h
 src/cocoaui/cocoaui.m
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -84,13 +84,12 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > src/gdrom/packet.h
 src/gettext.h
 src/gtkui/gtkcb.c
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+src/gtkui/gtk_cfg.c
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > src/gtkui/gtk_ctrl.c
 src/gtkui/gtk_debug.c
 src/gtkui/gtk_dump.c
 src/gtkui/gtk_gd.c
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-src/gtkui/gtk_hotkeys.c
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > src/gtkui/gtk_mmio.c
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-src/gtkui/gtk_path.c
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > src/gtkui/gtkui.c
 src/gtkui/gtkui.h
 src/gtkui/gtk_win.c
</pre></div>
<hr /><a name="file2" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>Makefile.am</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/Makefile.am
+++ lxdream/src/Makefile.am
@@ -98,16 +98,16 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 if GUI_GTK
 lxdream_SOURCES += gtkui/gtkui.c gtkui/gtkui.h \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-   gtkui/gtk_win.c gtkui/gtkcb.c \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+     gtkui/gtk_win.c gtkui/gtkcb.c <span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >gtkui/gtk_cfg.c </span>\
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         gtkui/gtk_mmio.c gtkui/gtk_debug.c gtkui/gtk_dump.c \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        gtkui/gtk_ctrl.c gtkui/gtk_path.c gtkui/gtk_gd.c \
-        gtkui/gtk_hotkeys.c drivers/net_glib.c drivers/video_gtk.c
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        gtkui/gtk_ctrl.c gtkui/gtk_gd.c \
+        drivers/net_glib.c drivers/video_gtk.c
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > endif
 
 if GUI_COCOA
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-lxdream_SOURCES += cocoaui/cocoaui.m cocoaui/cocoaui.h \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+lxdream_SOURCES += cocoaui/cocoaui.m cocoaui/cocoaui.h <span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >cocoaui/cocoa_cfg.m </span>\
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >    cocoaui/cocoa_win.m cocoaui/cocoa_gd.m cocoaui/cocoa_prefs.m \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-   cocoaui/cocoa_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >path.m cocoaui/cocoa_</span>ctrl.m cocoaui/paths_osx.m \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+     cocoaui/cocoa_ctrl.m cocoaui/paths_osx.m \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >    drivers/net_osx.m drivers/video_osx.m \
        drivers/mac_keymap.h drivers/mac_keymap.txt
 else
</pre></div>
<hr /><a name="file3" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>Makefile.in</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/Makefile.in
+++ lxdream/src/Makefile.in
@@ -52,14 +52,14 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 @BUILD_SH4X86_TRUE@am__append_3 = test/testsh4x86
 @GUI_GTK_TRUE@am__append_4 = gtkui/gtkui.c gtkui/gtkui.h \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@GUI_GTK_TRUE@     gtkui/gtk_win.c gtkui/gtkcb.c \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@GUI_GTK_TRUE@       gtkui/gtk_win.c gtkui/gtkcb.c <span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >gtkui/gtk_cfg.c </span>\
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_GTK_TRUE@        gtkui/gtk_mmio.c gtkui/gtk_debug.c gtkui/gtk_dump.c \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@GUI_GTK_TRUE@        gtkui/gtk_ctrl.c gtkui/gtk_path.c gtkui/gtk_gd.c \
-@GUI_GTK_TRUE@        gtkui/gtk_hotkeys.c drivers/net_glib.c drivers/video_gtk.c
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@GUI_GTK_TRUE@        gtkui/gtk_ctrl.c gtkui/gtk_gd.c \
+@GUI_GTK_TRUE@        drivers/net_glib.c drivers/video_gtk.c
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@GUI_COCOA_TRUE@am__append_5 = cocoaui/cocoaui.m cocoaui/cocoaui.h \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@GUI_COCOA_TRUE@am__append_5 = cocoaui/cocoaui.m cocoaui/cocoaui.h <span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >cocoaui/cocoa_cfg.m </span>\
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_COCOA_TRUE@   cocoaui/cocoa_win.m cocoaui/cocoa_gd.m cocoaui/cocoa_prefs.m \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@GUI_COCOA_TRUE@   cocoaui/cocoa_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >path.m cocoaui/cocoa_</span>ctrl.m cocoaui/paths_osx.m \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@GUI_COCOA_TRUE@     cocoaui/cocoa_ctrl.m cocoaui/paths_osx.m \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_COCOA_TRUE@   drivers/net_osx.m drivers/video_osx.m \
 @GUI_COCOA_TRUE@       drivers/mac_keymap.h drivers/mac_keymap.txt
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -174,13 +174,13 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >      x86dasm/dis-init.c x86dasm/dis-buf.c x86dasm/ansidecl.h \
        x86dasm/bfd.h x86dasm/dis-asm.h x86dasm/symcat.h \
        x86dasm/sysdep.h gtkui/gtkui.c gtkui/gtkui.h gtkui/gtk_win.c \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-   gtkui/gtkcb.c gtkui/gtk_mmio.c gtkui/gtk_debug.c \
-       gtkui/gtk_dump.c gtkui/gtk_ctrl.c gtkui/gtk_path.c \
-       gtkui/gtk_gd.c gtkui/gtk_hotkeys.c drivers/net_glib.c \
-       drivers/video_gtk.c cocoaui/cocoaui.m cocoaui/cocoaui.h \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+     gtkui/gtkcb.c gtkui/gtk_cfg.c gtkui/gtk_mmio.c \
+       gtkui/gtk_debug.c gtkui/gtk_dump.c gtkui/gtk_ctrl.c \
+       gtkui/gtk_gd.c drivers/net_glib.c drivers/video_gtk.c \
+       cocoaui/cocoaui.m cocoaui/cocoaui.h cocoaui/cocoa_cfg.m \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >    cocoaui/cocoa_win.m cocoaui/cocoa_gd.m cocoaui/cocoa_prefs.m \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-   cocoaui/cocoa_path.m cocoaui/cocoa_ctrl.m cocoaui/paths_osx.m \
-       drivers/net_osx.m drivers/video_osx.m drivers/mac_keymap.h \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+     cocoaui/cocoa_ctrl.m cocoaui/paths_osx.m drivers/net_osx.m \
+       drivers/video_osx.m drivers/mac_keymap.h \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >    drivers/mac_keymap.txt paths_unix.c drivers/video_gdk.c \
        drivers/video_glx.c drivers/video_glx.h drivers/video_nsgl.m \
        drivers/video_nsgl.h drivers/audio_osx.m drivers/audio_sdl.c \
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -199,20 +199,19 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_GTK_TRUE@am__objects_3 = lxdream-gtkui.$(OBJEXT) \
 @GUI_GTK_TRUE@ lxdream-gtk_win.$(OBJEXT) \
 @GUI_GTK_TRUE@ lxdream-gtkcb.$(OBJEXT) \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@GUI_GTK_TRUE@       lxdream-gtk_cfg.$(OBJEXT) \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_GTK_TRUE@     lxdream-gtk_mmio.$(OBJEXT) \
 @GUI_GTK_TRUE@ lxdream-gtk_debug.$(OBJEXT) \
 @GUI_GTK_TRUE@ lxdream-gtk_dump.$(OBJEXT) \
 @GUI_GTK_TRUE@ lxdream-gtk_ctrl.$(OBJEXT) \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@GUI_GTK_TRUE@     lxdream-gtk_path.$(OBJEXT) \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_GTK_TRUE@     lxdream-gtk_gd.$(OBJEXT) \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@GUI_GTK_TRUE@     lxdream-gtk_hotkeys.$(OBJEXT) \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_GTK_TRUE@     lxdream-net_glib.$(OBJEXT) \
 @GUI_GTK_TRUE@ lxdream-video_gtk.$(OBJEXT)
 @GUI_COCOA_TRUE@am__objects_4 = lxdream-cocoaui.$(OBJEXT) \
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@GUI_COCOA_TRUE@     lxdream-cocoa_cfg.$(OBJEXT) \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_COCOA_TRUE@   lxdream-cocoa_win.$(OBJEXT) \
 @GUI_COCOA_TRUE@       lxdream-cocoa_gd.$(OBJEXT) \
 @GUI_COCOA_TRUE@       lxdream-cocoa_prefs.$(OBJEXT) \
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@GUI_COCOA_TRUE@   lxdream-cocoa_path.$(OBJEXT) \
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @GUI_COCOA_TRUE@   lxdream-cocoa_ctrl.$(OBJEXT) \
 @GUI_COCOA_TRUE@       lxdream-paths_osx.$(OBJEXT) \
 @GUI_COCOA_TRUE@       lxdream-net_osx.$(OBJEXT) \
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -759,9 +758,9 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cd_none.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cd_osx.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cdi.Po@am__quote@
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cocoa_cfg.Po@am__quote@
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cocoa_ctrl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cocoa_gd.Po@am__quote@
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cocoa_path.Po@am__quote@
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cocoa_prefs.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cocoa_win.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-cocoaui.Po@am__quote@
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -785,13 +784,12 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gl_slsrc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-glrender.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-glutil.Po@am__quote@
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_cfg.Po@am__quote@
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_ctrl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_debug.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_dump.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_gd.Po@am__quote@
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_hotkeys.Po@am__quote@
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_mmio.Po@am__quote@
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_path.Po@am__quote@
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtk_win.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtkcb.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxdream-gtkui.Po@am__quote@
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -2100,6 +2098,20 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__fastdepCC_FALSE@    DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtkcb.obj `if test -f 'gtkui/gtkcb.c'; then $(CYGPATH_W) 'gtkui/gtkcb.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtkcb.c'; fi`
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+lxdream-gtk_cfg.o: gtkui/gtk_cfg.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_cfg.o -MD -MP -MF "$(DEPDIR)/lxdream-gtk_cfg.Tpo" -c -o lxdream-gtk_cfg.o `test -f 'gtkui/gtk_cfg.c' || echo '$(srcdir)/'`gtkui/gtk_cfg.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_cfg.Tpo" "$(DEPDIR)/lxdream-gtk_cfg.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_cfg.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='gtkui/gtk_cfg.c' object='lxdream-gtk_cfg.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_cfg.o `test -f 'gtkui/gtk_cfg.c' || echo '$(srcdir)/'`gtkui/gtk_cfg.c
+
+lxdream-gtk_cfg.obj: gtkui/gtk_cfg.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_cfg.obj -MD -MP -MF "$(DEPDIR)/lxdream-gtk_cfg.Tpo" -c -o lxdream-gtk_cfg.obj `if test -f 'gtkui/gtk_cfg.c'; then $(CYGPATH_W) 'gtkui/gtk_cfg.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_cfg.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_cfg.Tpo" "$(DEPDIR)/lxdream-gtk_cfg.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_cfg.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='gtkui/gtk_cfg.c' object='lxdream-gtk_cfg.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_cfg.obj `if test -f 'gtkui/gtk_cfg.c'; then $(CYGPATH_W) 'gtkui/gtk_cfg.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_cfg.c'; fi`
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > lxdream-gtk_mmio.o: gtkui/gtk_mmio.c
 @am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_mmio.o -MD -MP -MF "$(DEPDIR)/lxdream-gtk_mmio.Tpo" -c -o lxdream-gtk_mmio.o `test -f 'gtkui/gtk_mmio.c' || echo '$(srcdir)/'`gtkui/gtk_mmio.c; \
 @am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_mmio.Tpo" "$(DEPDIR)/lxdream-gtk_mmio.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_mmio.Tpo"; exit 1; fi
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -2156,20 +2168,6 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__fastdepCC_FALSE@    DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_ctrl.obj `if test -f 'gtkui/gtk_ctrl.c'; then $(CYGPATH_W) 'gtkui/gtk_ctrl.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_ctrl.c'; fi`
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-lxdream-gtk_path.o: gtkui/gtk_path.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_path.o -MD -MP -MF "$(DEPDIR)/lxdream-gtk_path.Tpo" -c -o lxdream-gtk_path.o `test -f 'gtkui/gtk_path.c' || echo '$(srcdir)/'`gtkui/gtk_path.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_path.Tpo" "$(DEPDIR)/lxdream-gtk_path.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_path.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='gtkui/gtk_path.c' object='lxdream-gtk_path.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_path.o `test -f 'gtkui/gtk_path.c' || echo '$(srcdir)/'`gtkui/gtk_path.c
-
-lxdream-gtk_path.obj: gtkui/gtk_path.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_path.obj -MD -MP -MF "$(DEPDIR)/lxdream-gtk_path.Tpo" -c -o lxdream-gtk_path.obj `if test -f 'gtkui/gtk_path.c'; then $(CYGPATH_W) 'gtkui/gtk_path.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_path.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_path.Tpo" "$(DEPDIR)/lxdream-gtk_path.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_path.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='gtkui/gtk_path.c' object='lxdream-gtk_path.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_path.obj `if test -f 'gtkui/gtk_path.c'; then $(CYGPATH_W) 'gtkui/gtk_path.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_path.c'; fi`
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > lxdream-gtk_gd.o: gtkui/gtk_gd.c
 @am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_gd.o -MD -MP -MF "$(DEPDIR)/lxdream-gtk_gd.Tpo" -c -o lxdream-gtk_gd.o `test -f 'gtkui/gtk_gd.c' || echo '$(srcdir)/'`gtkui/gtk_gd.c; \
 @am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_gd.Tpo" "$(DEPDIR)/lxdream-gtk_gd.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_gd.Tpo"; exit 1; fi
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -2184,20 +2182,6 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__fastdepCC_FALSE@    DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_gd.obj `if test -f 'gtkui/gtk_gd.c'; then $(CYGPATH_W) 'gtkui/gtk_gd.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_gd.c'; fi`
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-lxdream-gtk_hotkeys.o: gtkui/gtk_hotkeys.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_hotkeys.o -MD -MP -MF "$(DEPDIR)/lxdream-gtk_hotkeys.Tpo" -c -o lxdream-gtk_hotkeys.o `test -f 'gtkui/gtk_hotkeys.c' || echo '$(srcdir)/'`gtkui/gtk_hotkeys.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_hotkeys.Tpo" "$(DEPDIR)/lxdream-gtk_hotkeys.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_hotkeys.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='gtkui/gtk_hotkeys.c' object='lxdream-gtk_hotkeys.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_hotkeys.o `test -f 'gtkui/gtk_hotkeys.c' || echo '$(srcdir)/'`gtkui/gtk_hotkeys.c
-
-lxdream-gtk_hotkeys.obj: gtkui/gtk_hotkeys.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-gtk_hotkeys.obj -MD -MP -MF "$(DEPDIR)/lxdream-gtk_hotkeys.Tpo" -c -o lxdream-gtk_hotkeys.obj `if test -f 'gtkui/gtk_hotkeys.c'; then $(CYGPATH_W) 'gtkui/gtk_hotkeys.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_hotkeys.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-gtk_hotkeys.Tpo" "$(DEPDIR)/lxdream-gtk_hotkeys.Po"; else rm -f "$(DEPDIR)/lxdream-gtk_hotkeys.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='gtkui/gtk_hotkeys.c' object='lxdream-gtk_hotkeys.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lxdream-gtk_hotkeys.obj `if test -f 'gtkui/gtk_hotkeys.c'; then $(CYGPATH_W) 'gtkui/gtk_hotkeys.c'; else $(CYGPATH_W) '$(srcdir)/gtkui/gtk_hotkeys.c'; fi`
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > lxdream-net_glib.o: drivers/net_glib.c
 @am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lxdream-net_glib.o -MD -MP -MF "$(DEPDIR)/lxdream-net_glib.Tpo" -c -o lxdream-net_glib.o `test -f 'drivers/net_glib.c' || echo '$(srcdir)/'`drivers/net_glib.c; \
 @am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/lxdream-net_glib.Tpo" "$(DEPDIR)/lxdream-net_glib.Po"; else rm -f "$(DEPDIR)/lxdream-net_glib.Tpo"; exit 1; fi
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -2590,6 +2574,20 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__fastdepOBJC_FALSE@  DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepOBJC_FALSE@        $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o lxdream-cocoaui.obj `if test -f 'cocoaui/cocoaui.m'; then $(CYGPATH_W) 'cocoaui/cocoaui.m'; else $(CYGPATH_W) '$(srcdir)/cocoaui/cocoaui.m'; fi`
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+lxdream-cocoa_cfg.o: cocoaui/cocoa_cfg.m
+@am__fastdepOBJC_TRUE@ if $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -MT lxdream-cocoa_cfg.o -MD -MP -MF "$(DEPDIR)/lxdream-cocoa_cfg.Tpo" -c -o lxdream-cocoa_cfg.o `test -f 'cocoaui/cocoa_cfg.m' || echo '$(srcdir)/'`cocoaui/cocoa_cfg.m; \
+@am__fastdepOBJC_TRUE@ then mv -f "$(DEPDIR)/lxdream-cocoa_cfg.Tpo" "$(DEPDIR)/lxdream-cocoa_cfg.Po"; else rm -f "$(DEPDIR)/lxdream-cocoa_cfg.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    source='cocoaui/cocoa_cfg.m' object='lxdream-cocoa_cfg.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepOBJC_FALSE@        $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o lxdream-cocoa_cfg.o `test -f 'cocoaui/cocoa_cfg.m' || echo '$(srcdir)/'`cocoaui/cocoa_cfg.m
+
+lxdream-cocoa_cfg.obj: cocoaui/cocoa_cfg.m
+@am__fastdepOBJC_TRUE@ if $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -MT lxdream-cocoa_cfg.obj -MD -MP -MF "$(DEPDIR)/lxdream-cocoa_cfg.Tpo" -c -o lxdream-cocoa_cfg.obj `if test -f 'cocoaui/cocoa_cfg.m'; then $(CYGPATH_W) 'cocoaui/cocoa_cfg.m'; else $(CYGPATH_W) '$(srcdir)/cocoaui/cocoa_cfg.m'; fi`; \
+@am__fastdepOBJC_TRUE@ then mv -f "$(DEPDIR)/lxdream-cocoa_cfg.Tpo" "$(DEPDIR)/lxdream-cocoa_cfg.Po"; else rm -f "$(DEPDIR)/lxdream-cocoa_cfg.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    source='cocoaui/cocoa_cfg.m' object='lxdream-cocoa_cfg.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepOBJC_FALSE@        $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o lxdream-cocoa_cfg.obj `if test -f 'cocoaui/cocoa_cfg.m'; then $(CYGPATH_W) 'cocoaui/cocoa_cfg.m'; else $(CYGPATH_W) '$(srcdir)/cocoaui/cocoa_cfg.m'; fi`
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > lxdream-cocoa_win.o: cocoaui/cocoa_win.m
 @am__fastdepOBJC_TRUE@ if $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -MT lxdream-cocoa_win.o -MD -MP -MF "$(DEPDIR)/lxdream-cocoa_win.Tpo" -c -o lxdream-cocoa_win.o `test -f 'cocoaui/cocoa_win.m' || echo '$(srcdir)/'`cocoaui/cocoa_win.m; \
 @am__fastdepOBJC_TRUE@ then mv -f "$(DEPDIR)/lxdream-cocoa_win.Tpo" "$(DEPDIR)/lxdream-cocoa_win.Po"; else rm -f "$(DEPDIR)/lxdream-cocoa_win.Tpo"; exit 1; fi
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -2632,20 +2630,6 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @AMDEP_TRUE@@am__fastdepOBJC_FALSE@  DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepOBJC_FALSE@        $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o lxdream-cocoa_prefs.obj `if test -f 'cocoaui/cocoa_prefs.m'; then $(CYGPATH_W) 'cocoaui/cocoa_prefs.m'; else $(CYGPATH_W) '$(srcdir)/cocoaui/cocoa_prefs.m'; fi`
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-lxdream-cocoa_path.o: cocoaui/cocoa_path.m
-@am__fastdepOBJC_TRUE@ if $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -MT lxdream-cocoa_path.o -MD -MP -MF "$(DEPDIR)/lxdream-cocoa_path.Tpo" -c -o lxdream-cocoa_path.o `test -f 'cocoaui/cocoa_path.m' || echo '$(srcdir)/'`cocoaui/cocoa_path.m; \
-@am__fastdepOBJC_TRUE@ then mv -f "$(DEPDIR)/lxdream-cocoa_path.Tpo" "$(DEPDIR)/lxdream-cocoa_path.Po"; else rm -f "$(DEPDIR)/lxdream-cocoa_path.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    source='cocoaui/cocoa_path.m' object='lxdream-cocoa_path.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepOBJC_FALSE@        $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o lxdream-cocoa_path.o `test -f 'cocoaui/cocoa_path.m' || echo '$(srcdir)/'`cocoaui/cocoa_path.m
-
-lxdream-cocoa_path.obj: cocoaui/cocoa_path.m
-@am__fastdepOBJC_TRUE@ if $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -MT lxdream-cocoa_path.obj -MD -MP -MF "$(DEPDIR)/lxdream-cocoa_path.Tpo" -c -o lxdream-cocoa_path.obj `if test -f 'cocoaui/cocoa_path.m'; then $(CYGPATH_W) 'cocoaui/cocoa_path.m'; else $(CYGPATH_W) '$(srcdir)/cocoaui/cocoa_path.m'; fi`; \
-@am__fastdepOBJC_TRUE@ then mv -f "$(DEPDIR)/lxdream-cocoa_path.Tpo" "$(DEPDIR)/lxdream-cocoa_path.Po"; else rm -f "$(DEPDIR)/lxdream-cocoa_path.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    source='cocoaui/cocoa_path.m' object='lxdream-cocoa_path.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepOBJC_FALSE@    DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepOBJC_FALSE@        $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o lxdream-cocoa_path.obj `if test -f 'cocoaui/cocoa_path.m'; then $(CYGPATH_W) 'cocoaui/cocoa_path.m'; else $(CYGPATH_W) '$(srcdir)/cocoaui/cocoa_path.m'; fi`
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > lxdream-cocoa_ctrl.o: cocoaui/cocoa_ctrl.m
 @am__fastdepOBJC_TRUE@ if $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lxdream_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -MT lxdream-cocoa_ctrl.o -MD -MP -MF "$(DEPDIR)/lxdream-cocoa_ctrl.Tpo" -c -o lxdream-cocoa_ctrl.o `test -f 'cocoaui/cocoa_ctrl.m' || echo '$(srcdir)/'`cocoaui/cocoa_ctrl.m; \
 @am__fastdepOBJC_TRUE@ then mv -f "$(DEPDIR)/lxdream-cocoa_ctrl.Tpo" "$(DEPDIR)/lxdream-cocoa_ctrl.Po"; else rm -f "$(DEPDIR)/lxdream-cocoa_ctrl.Tpo"; exit 1; fi
</pre></div>
<hr /><a name="file4" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span id="added" class="pathname" style="font-family:monospace; float:right; background-color:#ddffdd;" >lxdream/src/cocoaui</span><br />
<div id="added" class="fileheader" style="margin-bottom:.5em; background-color:#ddffdd;" ><big><b>cocoa_cfg.m</b></big> <small id="info" style="color: #888888;" >added at d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/cocoaui/cocoa_cfg.m
+++ lxdream/src/cocoaui/cocoa_cfg.m
@@ -0,0 +1,383 @@
</small></pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+/**
+ * $Id$
+ *
+ * Construct and manage a configuration pane based on an underlying
+ * configuration group.
+ *
+ * Copyright (c) 2009 Nathan Keynes.
+ *
+ * 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.
+ */
+
+#include "cocoaui.h"
+#include "display.h"
+#include "lxpaths.h"
+#include "maple/maple.h"
+
+static void cocoa_config_keysym_hook(void *data, const gchar *keysym);
+
+@interface KeyBindingEditor (Private)
+- (void)updateKeysym: (const gchar *)sym;
+@end
+
+@implementation KeyBindingEditor
+- (id)init
+{
+    self = [super init];
+    isPrimed = NO;
+    lastValue = nil;
+    [self setFieldEditor: YES];
+    [self setEditable: FALSE];
+    return self;
+}
+- (void)dealloc
+{
+    if( lastValue != nil ) {
+        [lastValue release];
+        lastValue = nil;
+    }
+    [super dealloc];
+}
+- (void)setPrimed: (BOOL)primed
+{
+    if( primed != isPrimed ) {
+        isPrimed = primed;
+        if( primed ) {
+            lastValue = [[NSString stringWithString: [self string]] retain];
+            [self setString: @"<press key>"];
+            input_set_keysym_hook(cocoa_config_keysym_hook, self);
+        } else {
+            [lastValue release];
+            lastValue = nil;
+            input_set_keysym_hook(NULL,NULL);
+        }
+    }
+}
+- (BOOL)resignFirstResponder
+{
+    if( isPrimed ) {
+        [self setString: lastValue];
+        [self setPrimed: NO];
+    }
+    return [super resignFirstResponder];
+}
+- (void)fireBindingChanged
+{
+    id delegate = [self delegate];
+    if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
+        [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
+    }
+}
+
+- (void)updateKeysym: (const gchar *)sym
+{
+    if( sym != NULL ) {
+        [self setString: [NSString stringWithCString: sym]];
+        [self setPrimed: NO];
+        [self fireBindingChanged];
+    }
+}
+- (void)updateMousesym: (int)button
+{
+    gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, (button+1) );
+    if( keysym != NULL ) {
+        [self updateKeysym: keysym ];
+        g_free(keysym);
+    }
+}
+- (void)keyPressed: (int)keycode
+{
+    gchar *keysym = input_keycode_to_keysym(NULL, keycode);
+    if( keysym != NULL ) {
+        [self updateKeysym: keysym];
+        g_free(keysym);
+    }
+}
+- (void)insertText:(id)string
+{
+    // Do nothing
+}
+- (void)mouseDown: (NSEvent *)event
+{
+    if( isPrimed ) {
+        [self updateMousesym: 0];
+    } else {
+        [self setPrimed: YES];
+        [super mouseDown: event];
+    }
+}
+- (void)rightMouseDown: (NSEvent *)event
+{
+    if( isPrimed ) {
+        [self updateMousesym: 1];
+    }
+}
+- (void)otherMouseDown: (NSEvent *)event
+{
+    if( isPrimed ) {
+        [self updateMousesym: [event buttonNumber]];
+    }
+}
+- (void)keyDown: (NSEvent *) event
+{
+    NSString *chars = [event characters];
+    if( isPrimed ) {
+        if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
+            // Escape char = abort change
+            [self setString: lastValue];
+            [self setPrimed: NO];
+        } else {
+            [self keyPressed: ([event keyCode]+1)];
+        }
+    } else {
+        if( chars != NULL && [chars length] == 1 ) {
+            int ch = [chars characterAtIndex: 0];
+            switch( ch ) {
+            case 0x7F:
+                [self setString: @""];
+                [self fireBindingChanged];
+                break;
+            case '\r':
+                [self setPrimed: YES];
+                break;
+            default:
+                [super keyDown: event];
+                break;
+            }
+        } else {
+            [super keyDown: event];
+        }
+    }
+}
+- (void)flagsChanged: (NSEvent *) event
+{
+    if( isPrimed ) {
+        [self keyPressed: ([event keyCode]+1)];
+    }
+    [super flagsChanged: event];
+}
+@end
+
+static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
+{
+    KeyBindingEditor *editor = (KeyBindingEditor *)data;
+    [editor updateKeysym: keysym];
+}
+
+@implementation KeyBindingField
+@end
+
+/*************************** Configuration sub-view ***********************/
+
+#define KEYBINDING_SIZE 110
+#define DEFAULT_LABEL_WIDTH 150
+#define RIGHT_MARGIN 40
+
+@implementation ConfigurationView
+- (id)initWithFrame: (NSRect)frameRect
+{
+    return [self initWithFrame: frameRect configGroup: NULL];
+}
+- (id)initWithFrame: (NSRect)frameRect configGroup: (lxdream_config_group_t)config
+{
+    if( [super initWithFrame: frameRect] == nil ) {
+        return nil;
+    } else {
+        group = NULL;
+        labelWidth = DEFAULT_LABEL_WIDTH;
+        [self setConfigGroup: config];
+        return self;
+    }
+}
+- (BOOL)isFlipped
+{
+    return YES;
+}
+- (void)setLabelWidth: (int)width
+{
+    labelWidth = width;
+}
+- (void)removeSubviews
+{
+    [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
+}
+- (void)updateField: (int)binding
+{
+    const gchar *p = NULL;
+    NSString *val1 = [fields[binding][0] stringValue];
+     if( fields[binding][1] == NULL ) {
+         p = [val1 UTF8String];
+         lxdream_set_config_value( group, binding, p );
+     } else {
+         NSString *val2 = [fields[binding][1] stringValue];
+         char buf[ [val1 length] + [val2 length] + 2 ];
+
+         if( [val1 length] == 0 ) {
+             if( [val2 length] != 0 ) {
+                 p = [val2 UTF8String];
+             }
+         } else if( [val2 length] == 0 ) {
+             p = [val1 UTF8String];
+         } else {
+             sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
+             p = buf;
+         }
+         lxdream_set_config_value( group, binding, p );
+     }
+     lxdream_save_config();
+}
+
+- (void)controlTextDidChange: (NSNotification *)notify
+{
+    if( [[notify object] isKindOfClass: [KeyBindingField class]] ) {
+        [self updateField: [[notify object] tag]];
+    }
+}
+- (void)controlTextDidEndEditing: (NSNotification *)notify
+{
+    [self updateField: [[notify object] tag]];
+}
+
+- (void)openFileDialog: (id)sender
+{
+    int tag = [sender tag];
+    NSString *text = [fields[tag][0] stringValue];
+    NSOpenPanel *panel = [NSOpenPanel openPanel];
+    int result = [panel runModalForDirectory: nil file: nil types: nil];
+    if( result == NSOKButton && [[panel filenames] count] > 0 ) {
+        NSString *filename = [[panel filenames] objectAtIndex: 0];
+        gchar *str = get_escaped_path( [filename UTF8String] );
+        [fields[tag][0] setStringValue: [NSString stringWithUTF8String: str]];
+        lxdream_set_global_config_value(tag,str);
+        lxdream_save_config();
+    }
+}
+- (void)openDirDialog: (id)sender
+{
+    int tag = [sender tag];
+    NSString *text = [fields[tag][0] stringValue];
+    NSOpenPanel *panel = [NSOpenPanel openPanel];
+    [panel setCanChooseDirectories: YES];
+    [panel setCanCreateDirectories: YES];
+    int result = [panel runModalForDirectory: nil file: nil types: nil];
+    if( result == NSOKButton && [[panel filenames] count] > 0 ) {
+        NSString *filename = [[panel filenames] objectAtIndex: 0];
+        gchar *str = get_escaped_path( [filename UTF8String] );
+        [fields[tag][0] setStringValue: [NSString stringWithUTF8String: str]];
+        lxdream_set_global_config_value(tag,str);
+        lxdream_save_config();
+    }
+}
+
+- (void)setConfigGroup: (lxdream_config_group_t) config
+{
+    if( group == config ) {
+        return;
+    }
+    int width = [self frame].size.width;
+    int fieldWidth;
+
+    group = config;
+    [self removeSubviews];
+    if( config != NULL && config->params[0].key != NULL ) {
+        int count, i, y, x;
+
+        for( count=0; config->params[count].label != NULL; count++ );
+        int minWidth = labelWidth+KEYBINDING_SIZE*2+TEXT_GAP*4;
+        if( minWidth > width ) {
+            width = minWidth;
+        }
+        NSSize size = NSMakeSize( width, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
+        [self setFrameSize: size];
+        [self scrollRectToVisible: NSMakeRect(0,0,1,1)];
+
+        x = TEXT_GAP;
+        y = TEXT_GAP;
+        for( i=0; config->params[i].label != NULL; i++ ) {
+            /* Add label */
+            NSRect frame = NSMakeRect(x, y + 2, labelWidth, LABEL_HEIGHT);
+            NSTextField *label = cocoa_gui_add_label(self, NS_(config->params[i].label), frame);
+            [label setAlignment: NSRightTextAlignment];
+
+            switch(config->params[i].type) {
+            case CONFIG_TYPE_KEY:
+                frame = NSMakeRect( x + labelWidth + TEXT_GAP, y, KEYBINDING_SIZE, TEXT_HEIGHT);
+                fields[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
+                [fields[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
+                [fields[i][0] setTag: i];
+                [fields[i][0] setDelegate: self];
+                [self addSubview: fields[i][0]];
+
+                frame = NSMakeRect( x + labelWidth + KEYBINDING_SIZE + (TEXT_GAP*2), y, KEYBINDING_SIZE, TEXT_HEIGHT);
+                fields[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
+                [fields[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
+                [fields[i][1] setTag: i];
+                [fields[i][1] setDelegate: self];
+                [self addSubview: fields[i][1]];
+
+                if( config->params[i].value != NULL ) {
+                    gchar **parts = g_strsplit(config->params[i].value,",",3);
+                    if( parts[0] != NULL ) {
+                        [fields[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
+                        if( parts[1] != NULL ) {
+                            [fields[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
+                        }
+                    }
+                    g_strfreev(parts);
+                }
+                break;
+            case CONFIG_TYPE_FILE:
+            case CONFIG_TYPE_PATH:
+                fieldWidth = width - labelWidth - x - TEXT_HEIGHT - RIGHT_MARGIN - (TEXT_GAP*2);
+                frame = NSMakeRect( x + labelWidth + TEXT_GAP, y, fieldWidth, TEXT_HEIGHT );
+                NSTextField *field = [[NSTextField alloc] initWithFrame: frame];
+                [field setTag: i];
+                [field setStringValue: [NSString stringWithCString: config->params[i].value]];
+                [field setDelegate: self];
+                [field setAutoresizingMask: (NSViewMinYMargin|NSViewWidthSizable)];
+
+                frame = NSMakeRect( x+ labelWidth + fieldWidth + (TEXT_GAP*2), y,  TEXT_HEIGHT, TEXT_HEIGHT );
+                NSButton *button = [[NSButton alloc] initWithFrame: frame];
+                [button setTag: i];
+                [button setTitle: @""];
+                [button setButtonType: NSMomentaryPushInButton];
+                [button setBezelStyle: NSRoundedDisclosureBezelStyle];
+                [button setAutoresizingMask: (NSViewMinYMargin|NSViewMinXMargin)];
+                [button setTarget: self];
+                if( config->params[i].type == CONFIG_TYPE_FILE ) {
+                    [button setAction: @selector(openFileDialog:)];
+                } else {
+                    [button setAction: @selector(openDirDialog:)];
+                }
+
+                [self addSubview: label];
+                [self addSubview: field];
+                [self addSubview: button];
+                fields[i][0] = field;
+                fields[i][1] = NULL;
+            }
+            y += (TEXT_HEIGHT + TEXT_GAP);
+        }
+    } else {
+        [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
+    }
+}
+
+- (void)setDevice: (maple_device_t)newDevice
+{
+    if( newDevice != NULL && !MAPLE_IS_VMU(newDevice) ) {
+        [self setConfigGroup: maple_get_device_config(newDevice)];
+    } else {
+        [self setConfigGroup: NULL];
+    }
+}
+@end
+
</pre></div>
<hr /><a name="file5" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/cocoaui</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>cocoa_ctrl.m</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/cocoaui/cocoa_ctrl.m
+++ lxdream/src/cocoaui/cocoa_ctrl.m
@@ -31,291 +31,10 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #define LOAD_VMU_TAG -1
 #define CREATE_VMU_TAG -2
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-#define KEYBINDING_SIZE 110
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#define LABEL_WIDTH 85
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static void cocoa_config_keysym_hook(void *data, const gchar *keysym);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static gboolean cocoa_config_vmulist_hook(vmulist_change_type_t type, int idx, void *data);
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-
-@interface KeyBindingEditor (Private)
-- (void)updateKeysym: (const gchar *)sym;
-@end
-
-@implementation KeyBindingEditor
-- (id)init
-{
-    self = [super init];
-    isPrimed = NO;
-    lastValue = nil;
-    [self setFieldEditor: YES];
-    [self setEditable: FALSE];
-    return self;
-}
-- (void)dealloc
-{
-    if( lastValue != nil ) {
-        [lastValue release];
-        lastValue = nil;
-    }
-    [super dealloc];
-}
-- (void)setPrimed: (BOOL)primed
-{
-    if( primed != isPrimed ) {
-        isPrimed = primed;
-        if( primed ) {
-            lastValue = [[NSString stringWithString: [self string]] retain];
-            [self setString: @"<press key>"];
-            input_set_keysym_hook(cocoa_config_keysym_hook, self);            
-        } else {
-            [lastValue release];
-            lastValue = nil;
-            input_set_keysym_hook(NULL,NULL);
-        }
-    }
-}
-- (BOOL)resignFirstResponder
-{
-    if( isPrimed ) {
-        [self setString: lastValue];
-        [self setPrimed: NO];
-    }
-    return [super resignFirstResponder];
-}
-- (void)fireBindingChanged
-{
-    id delegate = [self delegate];
-    if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
-        [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
-    }
-}
-        
-- (void)updateKeysym: (const gchar *)sym
-{
-    if( sym != NULL ) {
-        [self setString: [NSString stringWithCString: sym]];
-        [self setPrimed: NO];
-        [self fireBindingChanged];
-    }
-}
-- (void)updateMousesym: (int)button
-{
-    gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, (button+1) );
-    if( keysym != NULL ) {
-        [self updateKeysym: keysym ];
-        g_free(keysym);
-    }
-}
-- (void)keyPressed: (int)keycode
-{
-    gchar *keysym = input_keycode_to_keysym(NULL, keycode);
-    if( keysym != NULL ) {
-        [self updateKeysym: keysym];
-        g_free(keysym);
-    }
-}
-- (void)insertText:(id)string
-{
-    // Do nothing
-}
-- (void)mouseDown: (NSEvent *)event
-{
-    if( isPrimed ) {
-        [self updateMousesym: 0];
-    } else {
-        [self setPrimed: YES];
-        [super mouseDown: event];
-    }
-}
-- (void)rightMouseDown: (NSEvent *)event
-{
-    if( isPrimed ) {
-        [self updateMousesym: 1];
-    }
-}
-- (void)otherMouseDown: (NSEvent *)event
-{
-    if( isPrimed ) {
-        [self updateMousesym: [event buttonNumber]];
-    }
-}
-- (void)keyDown: (NSEvent *) event
-{
-    NSString *chars = [event characters];
-    if( isPrimed ) {
-        if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
-            // Escape char = abort change
-            [self setString: lastValue];
-            [self setPrimed: NO];
-        } else {
-            [self keyPressed: ([event keyCode]+1)];
-        }
-    } else {
-        if( chars != NULL && [chars length] == 1 ) {
-            int ch = [chars characterAtIndex: 0];
-            switch( ch ) {
-            case 0x7F:
-                [self setString: @""]; 
-                [self fireBindingChanged];
-                break;
-            case '\r':
-                [self setPrimed: YES];
-                break;
-            default:
-                [super keyDown: event];
-                break;
-            }
-        } else {
-            [super keyDown: event];
-        }
-    }
-}
-- (void)flagsChanged: (NSEvent *) event
-{
-    if( isPrimed ) {
-        [self keyPressed: ([event keyCode]+1)];
-    }
-    [super flagsChanged: event];
-}
-@end
-
-static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
-{
-    KeyBindingEditor *editor = (KeyBindingEditor *)data;
-    [editor updateKeysym: keysym];
-}
-
-
-@implementation KeyBindingField
-@end
-
-/*************************** Key-binding sub-view ***********************/
-
-#define MAX_KEY_BINDINGS 32
-
-@interface ControllerKeyBindingView : NSView
-{
-    maple_device_t device;
-    NSTextField *field[MAX_KEY_BINDINGS][2];
-}
-- (id)initWithFrame: (NSRect)frameRect;
-- (void)setDevice: (maple_device_t)device;
-@end
-
-@implementation ControllerKeyBindingView
-- (id)initWithFrame: (NSRect)frameRect
-{
-    if( [super initWithFrame: frameRect] == nil ) {
-        return nil;
-    } else {
-        device = NULL;
-        return self;
-    }
-}
-- (BOOL)isFlipped
-{
-    return YES;
-}
-- (void)removeSubviews
-{
-    [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
-}
-- (void)controlTextDidChange: (NSNotification *)notify
-{
-    const gchar *p = NULL;
-    int binding = [[notify object] tag];
-    NSString *val1 = [field[binding][0] stringValue];
-    if( field[binding][1] == NULL ) {
-        p = [val1 UTF8String];
-    } else {
-        NSString *val2 = [field[binding][1] stringValue];
-        char buf[ [val1 length] + [val2 length] + 2 ];
-
-        if( [val1 length] == 0 ) {
-            if( [val2 length] != 0 ) {
-                p = [val2 UTF8String];
-            }
-        } else if( [val2 length] == 0 ) {
-            p = [val1 UTF8String];
-        } else {
-            sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
-            p = buf;
-        }
-    }
-    maple_set_device_config_value( device, binding, p ); 
-    lxdream_save_config();
-}
-- (void)setDevice: (maple_device_t)newDevice
-{
-    device = newDevice;
-    [self removeSubviews];
-    if( device != NULL && !MAPLE_IS_VMU(device) ) {
-        lxdream_config_entry_t config = maple_get_device_config(device);
-        if( config != NULL ) {
-            int count, i, y, x;
-
-            for( count=0; config[count].key != NULL; count++ );
-            x = TEXT_GAP;
-            NSSize size = NSMakeSize(85+KEYBINDING_SIZE*2+TEXT_GAP*4, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
-            [self setFrameSize: size];
-            [self scrollRectToVisible: NSMakeRect(0,0,1,1)]; 
-            y = TEXT_GAP;
-            for( i=0; config[i].key != NULL; i++ ) {
-                /* Add label */
-                NSRect frame = NSMakeRect(x, y + 2, 85, LABEL_HEIGHT);
-                NSTextField *label = cocoa_gui_add_label(self, NS_(config[i].label), frame);
-                [label setAlignment: NSRightTextAlignment];
-                
-                switch(config[i].type) {
-                case CONFIG_TYPE_KEY:
-                    frame = NSMakeRect( x + 85 + TEXT_GAP, y, KEYBINDING_SIZE, TEXT_HEIGHT);
-                    field[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
-                    [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
-                    [field[i][0] setTag: i];
-                    [field[i][0] setDelegate: self];
-                    [self addSubview: field[i][0]];
-
-                    frame = NSMakeRect( x + 85 + KEYBINDING_SIZE + (TEXT_GAP*2), y, KEYBINDING_SIZE, TEXT_HEIGHT);
-                    field[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
-                    [field[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
-                    [field[i][1] setTag: i];
-                    [field[i][1] setDelegate: self];
-                    [self addSubview: field[i][1]];
-
-                    if( config[i].value != NULL ) {
-                        gchar **parts = g_strsplit(config[i].value,",",3);
-                        if( parts[0] != NULL ) {
-                            [field[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
-                            if( parts[1] != NULL ) {
-                                [field[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
-                            }
-                        }
-                        g_strfreev(parts);
-                    }
-                    break;
-                case CONFIG_TYPE_FILE:
-                case CONFIG_TYPE_PATH:
-                    frame = NSMakeRect( x + 85 + TEXT_GAP, y, KEYBINDING_SIZE*2+TEXT_GAP, TEXT_HEIGHT);
-                    field[i][0] = [[NSTextField alloc] initWithFrame: frame];
-                    [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
-                    [field[i][0] setTag: i];
-                    [field[i][0] setDelegate: self];
-                    [self addSubview: field[i][0]];
-                    if( config[i].value != NULL ) {
-                        [field[i][0] setStringValue: [NSString stringWithCString: config[i].value]];
-                    }
-                    field[i][1] = NULL;
-                } 
-                y += (TEXT_HEIGHT + TEXT_GAP);
-            }
-        } else {
-            [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
-        }
-    } else {
-        [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
-    }
-}
-@end
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /*************************** Top-level controller pane ***********************/
 static NSButton *addRadioButton( int port, int sub, int x, int y, id parent )
 {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -385,10 +104,14 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     
     if( !primary ) {
         BOOL vmu_selected = NO;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        const char *vmu_name;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        const char *vmu_name<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" > = NULL</span>;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         if( device != NULL && MAPLE_IS_VMU(device) ) {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-            vmu_selected = YES;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >             vmu_name = MAPLE_VMU_NAME(device);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+            if( vmu_name == NULL ) {
+                device = NULL;
+            } else {
+                vmu_selected = YES;
+            }
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         }
         if( [popup numberOfItems] > 0 ) {
             [[popup menu] addItem: [NSMenuItem separatorItem]];
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -474,7 +197,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     struct maple_device *save_controller[MAPLE_MAX_DEVICES];
     NSButton *radio[MAPLE_MAX_DEVICES];
     NSPopUpButton *popup[MAPLE_MAX_DEVICES];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    ControllerKeyBindingView *key_bindings;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    ConfigurationView *key_bindings;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 + (LxdreamPrefsControllerPane *)new;
 - (void)vmulistChanged: (id)sender;
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -511,7 +234,8 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         NSRect bindingFrame = NSMakeRect(210+(TEXT_GAP*4), 0,
                    frameRect.size.width - (210+(TEXT_GAP*4)), [self contentHeight] + TEXT_GAP );
         NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: bindingFrame];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        key_bindings = [[Con<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >trollerKeyBinding</span>View alloc] initWithFrame: bindingFrame ];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        key_bindings = [[Con<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >figuration</span>View alloc] initWithFrame: bindingFrame ];
+        [key_bindings setLabelWidth: LABEL_WIDTH];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
         [scrollView setDocumentView: key_bindings];
         [scrollView setDrawsBackground: NO];
</pre></div>
<hr /><a name="file6" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span id="removed" class="pathname" style="font-family:monospace; float:right; background-color:#ffdddd;" >lxdream/src/cocoaui</span><br />
<div id="removed" class="fileheader" style="margin-bottom:.5em; background-color:#ffdddd;" ><big><b>cocoa_path.m</b></big> <small id="info" style="color: #888888;" >removed after 182cfe43c09e</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/cocoaui/cocoa_path.m
+++ lxdream/src/cocoaui/cocoa_path.m
@@ -1,135 +0,0 @@
</small></pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-/**
- * $Id$
- *
- * Construct and manage the paths configuration pane
- *
- * Copyright (c) 2008 Nathan Keynes.
- *
- * 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.
- */
-
-#include "cocoaui.h"
-#include "config.h"
-#include "lxpaths.h"
-#include "dreamcast.h"
-
-@interface LxdreamPrefsPathPane: LxdreamPrefsPane 
-{
-    NSTextField *fields[CONFIG_KEY_MAX];
-}
-+ (LxdreamPrefsPathPane *)new;
-@end
-
-@implementation LxdreamPrefsPathPane
-+ (LxdreamPrefsPathPane *)new
-{
-    return [[LxdreamPrefsPathPane alloc] initWithFrame: NSMakeRect(0,0,600,400)];
-}
-- (id)initWithFrame: (NSRect)frameRect
-{
-    if( [super initWithFrame: frameRect title: NS_("Paths")] == nil ) {
-        return nil;
-    } else {
-        int i;
-        int height = [self contentHeight] - TEXT_HEIGHT - TEXT_GAP;
-        int y = height;
-        
-        for( i=0; i<=CONFIG_KEY_MAX; i++ ) {
-            const struct lxdream_config_entry *entry = lxdream_get_global_config_entry(i);
-            if( entry->label != NULL ) {
-                NSRect frame = NSMakeRect( TEXT_GAP, y+2, 150, LABEL_HEIGHT );
-                NSTextField *label = cocoa_gui_add_label(self, NS_(entry->label), frame);
-                [label setAlignment: NSRightTextAlignment];
-
-                frame = NSMakeRect( 150 + (TEXT_GAP*2), y, 360, TEXT_HEIGHT ); 
-                NSTextField *field = [[NSTextField alloc] initWithFrame: frame];
-                [field setTag: i];
-                [field setStringValue: [NSString stringWithCString: entry->value]]; 
-                [field setDelegate: self];
-                [field setAutoresizingMask: (NSViewMinYMargin|NSViewWidthSizable)];
-                
-                frame = NSMakeRect( 510 + (TEXT_GAP*3), y,  TEXT_HEIGHT, TEXT_HEIGHT );
-                NSButton *button = [[NSButton alloc] initWithFrame: frame];
-                [button setTag: i];
-                [button setTitle: @""];
-                [button setButtonType: NSMomentaryPushInButton];
-                [button setBezelStyle: NSRoundedDisclosureBezelStyle];
-                [button setAutoresizingMask: (NSViewMinYMargin|NSViewMinXMargin)];
-                [button setTarget: self];
-                if( entry->type == CONFIG_TYPE_FILE ) {
-                    [button setAction: @selector(openFileDialog:)];
-                } else {
-                    [button setAction: @selector(openDirDialog:)];
-                }
-                
-                [self addSubview: label];
-                [self addSubview: field];
-                [self addSubview: button];
-                fields[i] = field;
-                y -= (TEXT_HEIGHT + TEXT_GAP);
-            }
-        }
-    }
-    return self;
-}
-- (void)controlTextDidEndEditing:(NSNotification *)notify
-{
-    int tag = [[notify object] tag];
-    const char *str = [[[notify object] stringValue] UTF8String];
-    const char *oldval = lxdream_get_global_config_value(tag);
-    if( str[0] == '\0' )
-        str = NULL;
-    if( oldval == NULL ? str != NULL : (str == NULL || strcmp(oldval,str) != 0 ) ) {   
-        lxdream_set_global_config_value(tag, str);
-        lxdream_save_config();
-        dreamcast_config_changed();
-    }
-}
-- (void)openFileDialog: (id)sender
-{
-    int tag = [sender tag];
-    NSString *text = [fields[tag] stringValue]; 
-    NSOpenPanel *panel = [NSOpenPanel openPanel];
-    int result = [panel runModalForDirectory: nil file: nil types: nil];
-    if( result == NSOKButton && [[panel filenames] count] > 0 ) {
-        NSString *filename = [[panel filenames] objectAtIndex: 0];
-        gchar *str = get_escaped_path( [filename UTF8String] );
-        [fields[tag] setStringValue: [NSString stringWithUTF8String: str]];
-        lxdream_set_global_config_value(tag,str);
-        lxdream_save_config();
-        dreamcast_config_changed();
-    }
-}
-- (void)openDirDialog: (id)sender
-{
-    int tag = [sender tag];
-    NSString *text = [fields[tag] stringValue]; 
-    NSOpenPanel *panel = [NSOpenPanel openPanel];
-    [panel setCanChooseDirectories: YES];
-    [panel setCanCreateDirectories: YES];
-    int result = [panel runModalForDirectory: nil file: nil types: nil];
-    if( result == NSOKButton && [[panel filenames] count] > 0 ) {
-        NSString *filename = [[panel filenames] objectAtIndex: 0];
-        gchar *str = get_escaped_path( [filename UTF8String] );
-        [fields[tag] setStringValue: [NSString stringWithUTF8String: str]];
-        lxdream_set_global_config_value(tag,str);
-        lxdream_save_config();
-        dreamcast_config_changed();
-    }
-}
-
-@end
-
-
-NSView *cocoa_gui_create_prefs_path_pane()
-{
-    return [LxdreamPrefsPathPane new];
-}
</pre></div>
<hr /><a name="file7" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/cocoaui</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>cocoa_prefs.m</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/cocoaui/cocoa_prefs.m
+++ lxdream/src/cocoaui/cocoa_prefs.m
@@ -54,6 +54,29 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         return self;
     }
 }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
+- (id)initWithFrame: (NSRect)frameRect title:(NSString *)title configGroup: (lxdream_config_group_t)group scrollable: (BOOL)scroll
+{
+    if( [self initWithFrame: frameRect title: title] == nil ) {
+        return nil;
+    }
+    ConfigurationView *view = [[ConfigurationView alloc] initWithFrame: frameRect
+                                                         configGroup: group ];
+    [view setAutoresizingMask: (NSViewWidthSizable|NSViewMinYMargin)];
+    if( scroll ) {
+        NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: NSMakeRect(0,0,frameRect.size.width,[self contentHeight]+TEXT_GAP)];
+        [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
+        [scrollView setDocumentView: view];
+        [scrollView setDrawsBackground: NO];
+        [scrollView setHasVerticalScroller: YES];
+        [scrollView setAutohidesScrollers: YES];
+        [self addSubview: scrollView];
+//        [view scrollRectToVisible: NSMakeRect(0,0,1,1)];
+    } else {
+        [self addSubview: view];
+    }
+    return self;
+}
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @end
 
 /**************************** Main preferences window ************************/
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -85,10 +108,18 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         [self setDelegate: self];
         [self setMinSize: NSMakeSize(400,300)];
         [self initToolbar];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        path_pane = cocoa_gui_create_prefs_path_pane();
-        ctrl_pane = cocoa_gui_create_prefs_controller_pane();
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        config_panes[0] = [[LxdreamPrefsPane alloc] initWithFrame: NSMakeRect(0,0,600,400)
+                             title: NS_("Paths")
+                             configGroup: lxdream_get_config_group(CONFIG_GROUP_GLOBAL)
+                             scrollable: YES];
+        config_panes[1] = cocoa_gui_create_prefs_controller_pane();
+        config_panes[2] = [[LxdreamPrefsPane alloc] initWithFrame: NSMakeRect(0,0,600,400)
+                             title: NS_("Hotkeys")
+                             configGroup: lxdream_get_config_group(CONFIG_GROUP_HOTKEYS)
+                             scrollable: YES];
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         binding_editor = nil;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        [self setContentView: <span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >path_pane</span>];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        [self setContentView: <span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >config_panes[0]</span>];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         return self;
     }
 }
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -124,9 +155,12 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     NSToolbarItem *ctrls = [self createToolbarItem: @"Controllers" label: @"Controllers"
                             tooltip: @"Configure controllers" icon: @"tb-ctrls"
                             action: @selector(controllers_action:)];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    toolbar_ids = [NSArray arrayWithObjects: @"Paths", @"Controllers", nil ];
-    toolbar_defaults = [NSArray arrayWithObjects: @"Paths", @"Controllers", nil ]; 
-    NSArray *values = [NSArray arrayWithObjects: paths, ctrls, nil ];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    NSToolbarItem *hotkeys=[self createToolbarItem: @"Hotkeys" label: @"Hotkeys"
+                            tooltip: @"Configure hotkeys" icon: @"tb-ctrls"
+                            action: @selector(hotkeys_action:)];
+    toolbar_ids = [NSArray arrayWithObjects: @"Paths", @"Controllers", @"Hotkeys", nil ];
+    toolbar_defaults = [NSArray arrayWithObjects: @"Paths", @"Controllers", @"Hotkeys", nil ];
+    NSArray *values = [NSArray arrayWithObjects: paths, ctrls, hotkeys, nil ];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     toolbar_items = [NSDictionary dictionaryWithObjects: values forKeys: toolbar_ids];
 
     [toolbar setDelegate: self];
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -138,11 +172,15 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 - (void)paths_action: (id)sender
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    [self setContentView: path_pane];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    [self setContentView: config_panes[0]];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 - (void)controllers_action: (id)sender
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    [self setContentView: c<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >trl_pane</span>];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    [self setContentView: c<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >onfig_panes[1]</span>];
+}
+- (void)hotkeys_action: (id)sender
+{
+    [self setContentView: config_panes[2]];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 /***************************** Toolbar methods ***************************/
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -171,7 +209,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    return [NSArray arrayWithObjects: @"Paths", @"Controllers", <span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >nil ]; </span>
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return [NSArray arrayWithObjects: @"Paths", @"Controllers", <span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >@"Hotkeys", nil ];</span>
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 - (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -187,4 +225,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         prefs_panel = [[LxdreamPrefsPanel alloc] initWithContentRect: NSMakeRect(0,0,640,540)];
     }
     [prefs_panel makeKeyAndOrderFront: prefs_panel];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-}
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >\ No newline at end of file
</small></pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+}
+
+/**************************** Simple config panels ***************************/
+
</pre></div>
<hr /><a name="file8" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/cocoaui</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>cocoaui.h</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/cocoaui/cocoaui.h
+++ lxdream/src/cocoaui/cocoaui.h
@@ -21,6 +21,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 #import <AppKit/AppKit.h>
 #include "lxdream.h"
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#include "config.h"
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #include "gui.h"
 #include "gettext.h"
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -75,6 +76,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     int headerHeight;
 }
 - (id)initWithFrame: (NSRect)frameRect title:(NSString *)title;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+- (id)initWithFrame: (NSRect)frameRect title:(NSString *)title configGroup: (lxdream_config_group_t)group scrollable: (BOOL)scroll;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > - (int)contentHeight;
 @end
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -90,12 +92,27 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 @end
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
+@interface ConfigurationView : NSView
+{
+    lxdream_config_group_t group;
+    int labelWidth;
+    NSTextField *fields[CONFIG_MAX_KEYS][2];
+}
+- (id)initWithFrame: (NSRect)frameRect;
+- (id)initWithFrame: (NSRect)frameRect configGroup: (lxdream_config_group_t)group;
+- (void)setLabelWidth: (int)width;
+- (void)setConfigGroup: (lxdream_config_group_t)group;
+- (void)setDevice: (struct maple_device *)device;
+@end
+
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > @interface LxdreamPrefsPanel : NSPanel
 {
     NSArray *toolbar_ids;
     NSArray *toolbar_defaults;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    NSView *config_panes[3];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     NSDictionary *toolbar_items;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    NSView *path_pane, *ctrl_pane;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     KeyBindingEditor *binding_editor;
 }
 - (id)initWithContentRect:(NSRect)contentRect;
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -107,11 +124,10 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > NSView *video_osx_create_drawable();
 void cocoa_gui_show_preferences();
 NSView *cocoa_gui_create_prefs_controller_pane();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-NSView *cocoa_gui_create_prefs_path_pane();
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 
 #ifdef __cplusplus
 }
 #endif
     
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-#endif /* lxdream_cocoaui_H */
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >\ No newline at end of file
</small></pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#endif /* lxdream_cocoaui_H */
</pre></div>
<hr /><a name="file9" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>config.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/config.c
+++ lxdream/src/config.c
@@ -25,22 +25,22 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #include <glib/gstrfuncs.h>
 #include <sys/types.h>
 #include <sys/stat.h>
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-#include "dream.h"
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#include "dream<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >cast</span>.h"
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #include "config.h"
 #include "lxpaths.h"
 #include "maple/maple.h"
 
 #define MAX_ROOT_GROUPS 16
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-extern struct lxdream_config_entry alsa_config[];
-extern struct lxdream_config_entry hotkeys_config[];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+extern struct lxdream_config_group hotkeys_group;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 gboolean lxdream_load_config_file( const gchar *filename );
 gboolean lxdream_save_config_file( const gchar *filename );
 gboolean lxdream_load_config_stream( FILE *f );
 gboolean lxdream_save_config_stream( FILE *f );
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static struct lxdream_config_entry global_config[] =
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static struct lxdream_config_group global_group =
+    { "global", dreamcast_config_changed, NULL, NULL,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >        {{ "bios", N_("Bios ROM"), CONFIG_TYPE_FILE, NULL },
         { "flash", N_("Flash ROM"), CONFIG_TYPE_FILE, NULL },
         { "default path", N_("Default disc path"), CONFIG_TYPE_PATH, "." },
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -51,31 +51,32 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         { "recent", NULL, CONFIG_TYPE_FILELIST, NULL },
         { "vmu", NULL, CONFIG_TYPE_FILELIST, NULL },
         { "quick state", NULL, CONFIG_TYPE_INTEGER, "0" },
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        { NULL, CONFIG_TYPE_NONE }};
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        { NULL, CONFIG_TYPE_NONE }}<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" > }</span>;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static struct lxdream_config_entry serial_config[] =
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static struct lxdream_config_group serial_group =
+    { "serial", NULL, NULL, NULL,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >        {{ "device", N_("Serial device"), CONFIG_TYPE_FILE, "/dev/ttyS1" },
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        { NULL, CONFIG_TYPE_NONE }};
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        { NULL, CONFIG_TYPE_NONE }}<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" > }</span>;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-struct lxdream_config_group lxdream_config_root[MAX_ROOT_GROUPS+1] = 
-       {{ "global", global_config },
-        { "controllers", NULL },
-        { "hotkeys", hotkeys_config },
-        { "serial", serial_config },
-        { NULL, CONFIG_TYPE_NONE }};
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+/**
+ * Dummy group for controllers (handled specially)
+ */
+static struct lxdream_config_group controllers_group =
+    { "controllers", NULL, NULL, NULL, {{NULL, CONFIG_TYPE_NONE}} };
+
+struct lxdream_config_group *lxdream_config_root[MAX_ROOT_GROUPS+1] = 
+       { &global_group, &controllers_group, &hotkeys_group, &serial_group, NULL };
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 static gchar *lxdream_config_load_filename = NULL;
 static gchar *lxdream_config_save_filename = NULL;
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-void lxdream_register_config_group( const gchar *key, lxdream_config_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >entry</span>_t group )
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+void lxdream_register_config_group( const gchar *key, lxdream_config_<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >group</span>_t group )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     int i;
     for( i=0; i<MAX_ROOT_GROUPS; i++ ) {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        if( lxdream_config_root[i].key == NULL ) {
-            lxdream_config_root[i].key = key;
-            lxdream_config_root[i].params = group;
-            lxdream_config_root[i+1].key = NULL;
-            lxdream_config_root[i+1].params = CONFIG_TYPE_NONE;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        if( lxdream_config_root[i] == NULL ) {
+            lxdream_config_root[i] = group;
+            lxdream_config_root[i+1] = NULL;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >             return;
         }
     }
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -129,16 +130,15 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     /* Construct platform dependent defaults */
     const gchar *user_path = get_user_data_path();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    global_config[CONFIG_BIOS_PATH].default_value = g_strdup_printf( "%s/dcboot.rom", user_path ); 
-    global_config[CONFIG_FLASH_PATH].default_value = g_strdup_printf( "%s/dcflash.rom", user_path ); 
-    global_config[CONFIG_SAVE_PATH].default_value = g_strdup_printf( "%s/save", user_path ); 
-    global_config[CONFIG_VMU_PATH].default_value = g_strdup_printf( "%s/vmu", user_path ); 
-    global_config[CONFIG_BOOTSTRAP].default_value = g_strdup_printf( "%s/IP.BIN", user_path ); 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    global_group.params[CONFIG_BIOS_PATH].default_value = g_strdup_printf( "%s/dcboot.rom", user_path ); 
+    global_group.params[CONFIG_FLASH_PATH].default_value = g_strdup_printf( "%s/dcflash.rom", user_path ); 
+    global_group.params[CONFIG_SAVE_PATH].default_value = g_strdup_printf( "%s/save", user_path ); 
+    global_group.params[CONFIG_VMU_PATH].default_value = g_strdup_printf( "%s/vmu", user_path ); 
+    global_group.params[CONFIG_BOOTSTRAP].default_value = g_strdup_printf( "%s/IP.BIN", user_path ); 
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     
     /* Copy defaults into main values */
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    struct lxdream_config_group *group = lxdream_config_root;
-    while( group->key != NULL ) {
-        struct lxdream_config_entry *param = group->params;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    for( int i=0; lxdream_config_root[i] != NULL; i++ ) {
+        struct lxdream_config_entry *param = lxdream_config_root[i]->params;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         if( param != NULL ) {
             while( param->key != NULL ) {
                 if( param->value != param->default_value ) {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -149,14 +149,53 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                 param++;
             }
         }
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        group++;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
     maple_detach_all();
 }
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+const gchar *lxdream_get_config_value( lxdream_config_group_t group, int key )
+{
+    return group->params[key].value;
+}
+
+
+gboolean lxdream_set_config_value( lxdream_config_group_t group, int key, const gchar *value )
+{
+    lxdream_config_entry_t param = &group->params[key];
+    if( param->value != value &&
+        (param->value == NULL || value == NULL || strcmp(param->value,value) != 0)  ) {
+
+        gchar *new_value = g_strdup(value);
+
+        /* If the group defines an on_change handler, it can block the change
+         * (ie due to an invalid setting).
+         */
+        if( group->on_change == NULL ||
+            group->on_change(group->data, group,key, param->value, new_value) ) {
+
+            /* Don't free the default value, but otherwise need to release the
+             * old value.
+             */
+            if( param->value != param->default_value && param->value != NULL ) {
+                free( param->value );
+            }
+            param->value = new_value;
+        } else { /* on_change handler said no. */
+            g_free(new_value);
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > const gchar *lxdream_get_global_config_value( int key )
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    return global_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >config</span>[key].value;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return global_<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >group.params</span>[key].value;
+}
+
+void lxdream_set_global_config_value( int key, const gchar *value )
+{
+    lxdream_set_config_value(&global_group, key, value);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 GList *lxdream_get_global_config_list_value( int key )
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -213,44 +252,38 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     return lxdream_get_global_config_value(key);
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-void lxdream_set_config_value( lxdream_config_entry_t param, const gchar *value )
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+struct lxdream_config_group * lxdream_get_config_group( int group )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    if( param->value != value ) {
-        if( param->value != param->default_value && param->value != NULL ) {
-            free( param->value );
-        }
-        param->value = g_strdup(value);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return lxdream_config_root[group];
+}
+
+void lxdream_copy_config_group( lxdream_config_group_t dest, lxdream_config_group_t src )
+{
+    int i;
+    for( i=0; src->params[i].key != NULL; i++ ) {
+        lxdream_set_config_value( dest, i, src->params[i].value );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-void lxdream_set_global_config_value( int key, const gchar *value )
-{
-    lxdream_set_config_value(&global_config[key], value);
-}
-
-const struct lxdream_config_entry * lxdream_get_global_config_entry( int key )
-{
-    return &global_config[key];
-}
-
-gboolean lxdream_set_group_value( lxdream_config_group_t group, const gchar *key, const gchar *value )
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+void lxdream_clone_config_group( lxdream_config_group_t dest, lxdream_config_group_t src )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     int i;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    for( i=0; group->params[i].key != NULL; i++ ) {
-        if( strcasecmp( group->params[i].key, key ) == 0 ) {
-            lxdream_set_config_value( &group->params[i], value );
-            return TRUE;
-        }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
+    dest->key = src->key;
+    dest->on_change = NULL;
+    dest->key_binding = NULL;
+    dest->data = NULL;
+    for( i=0; src->params[i].key != NULL; i++ ) {
+        dest->params[i].key = src->params[i].key;
+        dest->params[i].label = src->params[i].label;
+        dest->params[i].type = src->params[i].type;
+        dest->params[i].tag = src->params[i].tag;
+        dest->params[i].default_value = src->params[i].default_value;
+        dest->params[i].value = NULL;
+        lxdream_set_config_value( dest, i, src->params[i].value );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    return FALSE;
-}
-
-void lxdream_copy_config_list( lxdream_config_entry_t dest, lxdream_config_entry_t src )
-{
-    int i;
-    for( i=0; src[i].key != NULL; i++ ) {
-        lxdream_set_config_value( &dest[i], src[i].value );
-    }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    dest->params[i].key = NULL;
+    dest->params[i].label = NULL;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 gboolean lxdream_load_config( )
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -296,9 +329,9 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
 
     char buf[512];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    int maple_device = -1, maple_subdevice = -1;
-    struct lxdream_config_group devgroup;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    int maple_device = -1, maple_subdevice = -1, i;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     struct lxdream_config_group *group = NULL;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    struct lxdream_config_group *top_group = NULL;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     maple_device_t device = NULL;
     lxdream_set_default_config();
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -309,17 +342,14 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         if( *buf == '[' ) {
             char *p = strchr(buf, ']');
             if( p != NULL ) {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                struct lxdream_config_group *tmp_group;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                 maple_device = maple_subdevice = -1;
                 *p = '\0';
                 g_strstrip(buf+1);
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                tmp_group = &lxdream_config_root[0];
-                while( tmp_group->key != NULL ) {
-                    if( strcasecmp(tmp_group->key, buf+1) == 0 ) {
-                        group = tmp_group;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                for( i=0; lxdream_config_root[i] != NULL; i++ ) {
+                    if( strcasecmp(lxdream_config_root[i]->key, buf+1) == 0 ) {
+                        top_group = group = lxdream_config_root[i];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                         break;
                     }
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                    tmp_group++;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                 }
             }
         } else if( group != NULL ) {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -330,7 +360,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                 value++;
                 g_strstrip(buf);
                 g_strstrip(value);
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                if( strcmp(group->key,"controllers") == 0  ) {
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                if( top_group == &controllers_group ) {
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                     if( g_strncasecmp( buf, "device ", 7 ) == 0 ) {
                         maple_device = strtoul( buf+7, NULL, 0 );
                         if( maple_device < 0 || maple_device > 3 ) {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -342,10 +372,8 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                         if( device == NULL ) {
                             ERROR( "Unrecognized device '%s'", value );
                         } else {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                            devgroup.key = "controllers";
-                            devgroup.params = maple_get_device_config(device);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                            group = maple_get_device_config(device);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                             maple_attach_device( device, maple_device, maple_subdevice );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                            group = &devgroup;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                         }
                         continue;
                     } else if( g_strncasecmp( buf, "subdevice ", 10 ) == 0 ) {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -357,10 +385,8 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                         } else if( (device = maple_new_device(value)) == NULL ) {
                             ERROR( "Unrecognized subdevice '%s'", value );
                         } else {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                            devgroup.key = "controllers";
-                            devgroup.params = maple_get_device_config(device);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                            group = maple_get_device_config(device);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                             maple_attach_device( device, maple_device, maple_subdevice );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                            group = &devgroup;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                         }
                         continue;
                     }
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -393,20 +419,11 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 gboolean lxdream_save_config_stream( FILE *f )
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    struct lxdream_config_group *group = &lxdream_config_root[0];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    int i;
+    for( i=0; lxdream_config_root[i] != NULL; i++ ) {
+        fprintf( f, "[%s]\n", lxdream_config_root[i]->key );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    while( group->key != NULL ) {
-        struct lxdream_config_entry *entry = group->params;
-        fprintf( f, "[%s]\n", group->key );
-
-        if( entry != NULL ) {
-            while( entry->key != NULL ) {
-                if( entry->value != NULL ) {
-                    fprintf( f, "%s = %s\n", entry->key, entry->value );
-                }
-                entry++;
-            }
-        } else if( strcmp(group->key, "controllers") == 0 ) {
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        if( lxdream_config_root[i] == &controllers_group ) {
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >             int i,j;
             for( i=0; i<4; i++ ) {
                 for( j=0; j<6; j++ ) {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -416,7 +433,9 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                             fprintf( f, "Device %d = %s\n", i, dev->device_class->name );
                         else 
                             fprintf( f, "Subdevice %d = %s\n", j, dev->device_class->name );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                        if( dev->get_config != NULL && ((entry = dev->get_config(dev)) != NULL) ) {
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                        lxdream_config_group_t group = maple_get_device_config(dev);
+                        if( group != NULL ) {
+                            lxdream_config_entry_t entry = group->params;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                             while( entry->key != NULL ) {
                                 if( entry->value != NULL ) {
                                     fprintf( f, "%*c%s = %s\n", j==0?4:8, ' ',entry->key, entry->value );
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -427,9 +446,16 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                     }
                 }
             }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        } else {
+            struct lxdream_config_entry *entry = lxdream_config_root[i]->params;
+            while( entry->key != NULL ) {
+                if( entry->value != NULL ) {
+                    fprintf( f, "%s = %s\n", entry->key, entry->value );
+                }
+                entry++;
+            }
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         }
         fprintf( f, "\n" );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        group++;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
     return TRUE;
 }
</pre></div>
<hr /><a name="file10" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>config.h</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/config.h
+++ lxdream/src/config.h
@@ -21,12 +21,15 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 #include <glib/gtypes.h>
 #include <glib/glist.h>
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#include "lxdream.h"
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #include "gettext.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#define CONFIG_MAX_KEYS 24
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #define CONFIG_TYPE_NONE 0
 #define CONFIG_TYPE_FILE 1
 #define CONFIG_TYPE_PATH 2
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -39,14 +42,32 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > typedef struct lxdream_config_entry {
     const gchar *key;
     const gchar *label; // i18n 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    <span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >const </span>int type;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    int type;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     const gchar *default_value;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    uint32_t tag;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     gchar *value;
 } *lxdream_config_entry_t;
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+struct lxdream_config_group;
+
+/**
+ * Callback invoked for key presses (key-up/key-down).
+ */
+typedef void (*key_binding_t)( void *data, uint32_t value, uint32_t pressure, gboolean isKeyDown );
+
+/**
+ * Callback invoked immediately before updating a configuration item.
+ * @return FALSE to abort the change (ie invalid value), or TRUE to proceed normally. 
+ */
+typedef gboolean (*config_change_callback_t)( void *data, struct lxdream_config_group *group, unsigned item,
+                                   const gchar *oldval, const gchar *newval );
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > typedef struct lxdream_config_group {
     const gchar *key;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    struct lxdream_config_entry *params;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    config_change_callback_t on_change;
+    key_binding_t key_binding;
+    void *data;
+    struct lxdream_config_entry params[CONFIG_MAX_KEYS];
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > } *lxdream_config_group_t;
 
 #define CONFIG_BIOS_PATH 0
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -61,17 +82,20 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #define CONFIG_QUICK_STATE 9
 #define CONFIG_KEY_MAX CONFIG_QUICK_STATE
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-extern struct lxdream_config_group lxdream_config_root[];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#define CONFIG_GROUP_GLOBAL 0
+#define CONFIG_GROUP_HOTKEYS 2
+#define CONFIG_GROUP_SERIAL 3
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 /* Global config values */
 const gchar *lxdream_get_global_config_value( int key );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-const struct lxdream_config_entry * lxdream_get_global_config_entry( int key );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+struct lxdream_config_group * lxdream_get_config_group( int group );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > void lxdream_set_global_config_value( int key, const gchar *value );
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-void lxdream_register_config_group( const gchar *key, lxdream_config_entry_t group ); 
-void lxdream_set_config_value( lxdream_config_entry_t entry, const gchar *value );
-gboolean lxdream_set_group_value( lxdream_config_group_t group, const gchar *key, const gchar *value );
-void lxdream_copy_config_list( lxdream_config_entry_t dest, lxdream_config_entry_t src );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+void lxdream_register_config_group( const gchar *key, lxdream_config_group_t group ); 
+gboolean lxdream_set_config_value( lxdream_config_group_t group, int key, const gchar *value );
+void lxdream_copy_config_group( lxdream_config_group_t dest, lxdream_config_group_t src );
+void lxdream_clone_config_group( lxdream_config_group_t dest, lxdream_config_group_t src );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 /**
  * Return a fully expanded path value for a key - this performs substitutions
</pre></div>
<hr /><a name="file11" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>display.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/display.c
+++ lxdream/src/display.c
@@ -56,6 +56,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     input_key_callback_t callback;
     void *data;
     uint32_t value;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    lxdream_config_group_t group;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     struct keymap_entry *next; // allow chaining
 } *keymap_entry_t;
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -63,6 +64,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     gboolean relative;
     input_mouse_callback_t callback;
     void *data;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    const lxdream_config_group_t group;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     struct mouse_entry *next;
 } *mouse_entry_t;
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -293,6 +295,38 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     g_strfreev(strv);
 }
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+int input_register_keygroup( lxdream_config_group_t group)
+{
+    int i;
+    int result = 0;
+    for( i=0; group->params[i].key != NULL; i++ ) {
+        if( group->params[i].type == CONFIG_TYPE_KEY ) {
+            if( input_register_key( group->params[i].value, group->key_binding, group->data, group->params[i].tag ) ) {
+                result++;
+            }
+        }
+    }
+    return result;
+}
+
+void input_unregister_keygroup( lxdream_config_group_t group )
+{
+    int i;
+    for( i=0; group->params[i].key != NULL; i++ ) {
+        if( group->params[i].type == CONFIG_TYPE_KEY ) {
+            input_unregister_key( group->params[i].value, group->key_binding, group->data, group->params[i].tag );
+        }
+    }
+}
+
+gboolean input_keygroup_changed( void *data, lxdream_config_group_t group, unsigned key,
+                                 const gchar *oldval, const gchar *newval )
+{
+    input_unregister_key( oldval, group->key_binding, group->data, group->params[key].tag );
+    input_register_key( newval, group->key_binding, group->data, group->params[key].tag );
+    return TRUE;
+}
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > gboolean input_register_keyboard_hook( input_key_callback_t callback,
                               void *data )
 {
</pre></div>
<hr /><a name="file12" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>display.h</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/display.h
+++ lxdream/src/display.h
@@ -27,6 +27,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #include <glib.h>
 #include "lxdream.h"
 #include "gettext.h"
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#include "config.h"
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #ifdef APPLE_BUILD
 #include <OpenGL/gl.h>
 #include <OpenGL/glext.h>
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -230,7 +231,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /* Pressure is 0..127  (allowing a joystick to be defined as two half-axes of 7- bits each) */
 #define MAX_PRESSURE 0x7F
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-typedef void (*input_key_callback_t)( void *data, uint32_t value, uint32_t pressure, gboolean isKeyDown );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+typedef key_binding_t input_key_callback_t;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 /**
  * Callback to receive mouse input events
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -252,6 +253,11 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > void input_unregister_key( const gchar *keysym, input_key_callback_t callback,
                            void *data, uint32_t value );
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+gboolean input_register_keygroup( lxdream_config_group_t group );
+void input_unregister_keygroup( lxdream_config_group_t group );
+gboolean input_keygroup_changed( void *data, lxdream_config_group_t group, unsigned key,
+                             const gchar *oldval, const gchar *newval );
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /**
  * Register a hook to receive all keyboard input events
  */
</pre></div>
<hr /><a name="file13" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>dreamcast.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/dreamcast.c
+++ lxdream/src/dreamcast.c
@@ -126,17 +126,39 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     g_free(flash_path);
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-void dreamcast_config_changed(void)
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+gboolean dreamcast_load_bios( const gchar *filename )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    char *bios_path = lxdream_get_global_config_path_value(CONFIG_BIOS_PATH);
-    char *flash_path = lxdream_get_global_config_path_value(CONFIG_FLASH_PATH);
-    dreamcast_has_bios = mem_load_rom( dc_boot_rom, bios_path, 2 MB, 0x89f2b1a1 );
-    if( flash_path != NULL && flash_path[0] != '\0' ) {
-        mem_load_block( flash_path, 0x00200000, 0x00020000 );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    dreamcast_has_bios = mem_load_rom( dc_boot_rom, filename, 2 MB, 0x89f2b1a1 );
+    return dreamcast_has_bios;
+}
+
+gboolean dreamcast_load_flash( const gchar *filename )
+{
+    if( filename != NULL && filename[0] != '\0' ) {
+        return mem_load_block( filename, 0x00200000, 0x00020000 ) == 0;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    g_free(bios_path);
-    g_free(flash_path);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return FALSE;
+}
+
+
+gboolean dreamcast_config_changed(void *data, struct lxdream_config_group *group, unsigned item,
+                                       const gchar *oldval, const gchar *newval)
+{
+    gchar *tmp;
+    switch(item) {
+    case CONFIG_BIOS_PATH:
+        tmp = get_expanded_path(newval);
+        dreamcast_load_bios(tmp);
+        g_free(tmp);
+        break;
+    case CONFIG_FLASH_PATH:
+        tmp = get_expanded_path(newval);
+        dreamcast_load_flash(tmp);
+        g_free(tmp);
+        break;
+    }
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     reset_gui_paths();
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return TRUE;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 void dreamcast_save_flash()
</pre></div>
<hr /><a name="file14" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>dreamcast.h</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/dreamcast.h
+++ lxdream/src/dreamcast.h
@@ -35,6 +35,8 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #define XLAT_TEMP_CACHE_SIZE 2 MB
 #define XLAT_OLD_CACHE_SIZE 8 MB
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+struct lxdream_config_group; // Forward declaration
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > void dreamcast_configure(void);
 void dreamcast_configure_aica_only(void);
 void dreamcast_init(void);
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -44,9 +46,9 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > void dreamcast_set_exit_on_stop( gboolean flag );
 void dreamcast_stop(void);
 void dreamcast_shutdown(void);
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-void dreamcast_config_changed(void);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > gboolean dreamcast_is_running(void);
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+gboolean dreamcast_config_changed(void *data, struct lxdream_config_group *group, unsigned item,
+                                       const gchar *oldval, const gchar *newval);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /**
  * Return if it's possible to start the VM - currently this requires 
  * a) A configured system
</pre></div>
<hr /><a name="file15" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span id="added" class="pathname" style="font-family:monospace; float:right; background-color:#ddffdd;" >lxdream/src/gtkui</span><br />
<div id="added" class="fileheader" style="margin-bottom:.5em; background-color:#ddffdd;" ><big><b>gtk_cfg.c</b></big> <small id="info" style="color: #888888;" >added at d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/gtkui/gtk_cfg.c
+++ lxdream/src/gtkui/gtk_cfg.c
@@ -0,0 +1,330 @@
</small></pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+/**
+ * $Id$
+ *
+ * Configuration pane to display a configuration group
<a name="task1" />+ * <span class="task" style="background-color:#ffff00;" >TODO</span>:
+ *
+ * Copyright (c) 2009 Nathan Keynes.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "lxdream.h"
+#include "config.h"
+#include "lxpaths.h"
+#include "display.h"
+#include "gtkui/gtkui.h"
+
+struct config_data {
+    lxdream_config_group_t config;
+    GtkWidget *fields[CONFIG_MAX_KEYS][2];
+};
+
+/**
+ * Update the configuration data for the current value of the given field.
+ */
+static gboolean config_text_changed( GtkWidget *field, gpointer p )
+{
+    GtkWidget *panel = field->parent;
+    struct config_data *data= (struct config_data *)gtk_object_get_data( GTK_OBJECT(panel), "config_data" );
+    int tag = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(field), "tag" ) );
+
+    char buf[64];
+    GtkWidget *entry1, *entry2;
+    const gchar *key1 = NULL, *key2 = NULL;
+
+    if( data->fields[tag][0] != NULL ) {
+        key1 = gtk_entry_get_text(GTK_ENTRY(data->fields[tag][0]));
+    }
+    if( data->fields[tag][1] != NULL ) {
+        key2 = gtk_entry_get_text(GTK_ENTRY(data->fields[tag][1]));
+    }
+
+    if( key1 == NULL || key1[0] == '\0') {
+        lxdream_set_config_value( data->config, tag, key2 );
+    } else if( key2 == NULL || key2[0] == '\0') {
+        lxdream_set_config_value( data->config, tag, key1 );
+    } else {
+        char buf[strlen(key1) + strlen(key2) + 3];
+        snprintf( buf, sizeof(buf), "%s, %s", key1, key2 );
+        lxdream_set_config_value( data->config, tag, buf );
+    }
+    return TRUE;
+}
+
+/**
+ * Reset the fields (identified by one of the widgets in the field) back to it's
+ * value in the config group.
+ */
+static void config_text_reset( GtkWidget *field )
+{
+    GtkWidget *panel = field->parent;
+    struct config_data *data= (struct config_data *)gtk_object_get_data( GTK_OBJECT(panel), "config_data" );
+    int tag = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(field), "tag" ) );
+
+    const gchar *value = data->config->params[tag].value;
+    if( value == NULL ) {
+        value = "";
+    }
+
+    if( data->fields[tag][0] == NULL ) {
+        if( data->fields[tag][1] != NULL ) {
+            gtk_entry_set_text( GTK_ENTRY(data->fields[tag][1]), value );
+        }
+    } else if( data->fields[tag][1] == NULL ) {
+        gtk_entry_set_text( GTK_ENTRY(data->fields[tag][0]), value );
+    } else { /* Split between two fields */
+        gchar *v1 = "", *v2 = "";
+        gchar **parts = g_strsplit(value,",",3);
+        if( parts[0] != NULL ) {
+            v1 = parts[0];
+            if( parts[1] != NULL ) {
+                v2 = parts[1];
+            }
+
+        }
+        gtk_entry_set_text( GTK_ENTRY(data->fields[tag][0]), v1 );
+        gtk_entry_set_text( GTK_ENTRY(data->fields[tag][1]), v2 );
+        g_strfreev(parts);
+    }
+}
+
+static void config_set_field( void *p, const gchar *keysym )
+{
+    GtkWidget *field = GTK_WIDGET(p);
+    GtkWidget *panel = field->parent;
+
+    gtk_entry_set_text( GTK_ENTRY(field), keysym );
+    g_object_set_data( G_OBJECT(field), "keypress_mode", GINT_TO_POINTER(FALSE) );
+    input_set_keysym_hook(NULL, NULL);
+    config_text_changed( field, NULL );
+}
+
+static gboolean config_key_buttonpress( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
+{
+    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
+    if( keypress_mode ) {
+        gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, event->button);
+        if( keysym != NULL ) {
+            config_set_field( widget, keysym );
+            g_free(keysym);
+        }
+        return TRUE;
+    } else {
+        gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
+        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
+        input_set_keysym_hook( (display_keysym_callback_t)config_set_field, widget);
+        gtk_widget_grab_focus( widget );
+    }
+    return FALSE;
+}
+
+static gboolean config_key_keypress( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
+{
+    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
+    if( keypress_mode ) {
+        if( event->keyval == GDK_Escape ) {
+            config_text_reset( widget );
+            return TRUE;
+        }
+        GdkKeymap *keymap = gdk_keymap_get_default();
+        guint keyval;
+
+        gdk_keymap_translate_keyboard_state( keymap, event->hardware_keycode, 0, 0, &keyval,
+                                             NULL, NULL, NULL );
+        config_set_field( widget, gdk_keyval_name(keyval) );
+        return TRUE;
+    } else {
+        switch( event->keyval ) {
+        case GDK_Return:
+        case GDK_KP_Enter:
+            gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
+            g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
+            input_set_keysym_hook((display_keysym_callback_t)config_set_field, widget);
+            return TRUE;
+        case GDK_BackSpace:
+        case GDK_Delete:
+            config_set_field( widget, "" );
+            return TRUE;
+        }
+        return FALSE;
+    }
+}
+
+static gboolean config_key_unfocus( GtkWidget *widget, gpointer user_data )
+{
+    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
+    if( keypress_mode ) {
+        /* We've lost focus while waiting for a key binding - restore the old value */
+        config_text_reset(widget);
+        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
+        input_set_keysym_hook(NULL,NULL);
+    }
+    return TRUE;
+}
+
+static gboolean path_file_button_clicked( GtkWidget *button, gpointer user_data )
+{
+    GtkWidget *entry = GTK_WIDGET(user_data);
+    GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
+            GTK_FILE_CHOOSER_ACTION_OPEN,
+            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+            NULL );
+    gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
+    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
+    gtk_window_set_modal( GTK_WINDOW(file), TRUE );
+    gtk_widget_show_all( file );
+    gint result = gtk_dialog_run(GTK_DIALOG(file));
+    g_free(filename);
+    if( result == GTK_RESPONSE_ACCEPT ) {
+        filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
+        config_set_field( entry, filename );
+        g_free(filename);
+    }
+    gtk_widget_destroy(file);
+    return TRUE;
+}
+
+static gboolean path_dir_button_clicked( GtkWidget *button, gpointer user_data )
+{
+    GtkWidget *entry = GTK_WIDGET(user_data);
+    GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
+            GTK_FILE_CHOOSER_ACTION_OPEN,
+            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+            NULL );
+    gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
+    gtk_file_chooser_set_action( GTK_FILE_CHOOSER(file), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
+    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
+    gtk_window_set_modal( GTK_WINDOW(file), TRUE );
+    gtk_widget_show_all( file );
+    gint result = gtk_dialog_run(GTK_DIALOG(file));
+    g_free(filename);
+    if( result == GTK_RESPONSE_ACCEPT ) {
+        filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
+        config_set_field( entry, filename );
+        g_free(filename);
+    }
+    gtk_widget_destroy(file);
+    return TRUE;
+}
+
+
+static void lxdream_configuration_panel_destroy( GtkWidget *panel, gpointer data )
+{
+    input_set_keysym_hook(NULL, NULL);
+}
+
+static GtkWidget *gtk_configuration_panel_new( lxdream_config_group_t conf )
+{
+    int count, i;
+    for( count=0; conf->params[count].label != NULL; count++ );
+
+    GtkWidget *table = gtk_table_new( count, 5, FALSE );
+    struct config_data *data = g_malloc0( sizeof(struct config_data) );
+    data->config = conf;
+    GList *focus_chain = NULL;
+    gtk_object_set_data_full( GTK_OBJECT(table), "config_data", data, g_free );
+    g_signal_connect( table, "destroy_event", G_CALLBACK(lxdream_configuration_panel_destroy), NULL );
+    for( i=0; conf->params[i].label != NULL; i++ ) {
+        GtkWidget *text, *text2, *button;
+        int x=0;
+        int y=i;
+        gtk_table_attach( GTK_TABLE(table), gtk_label_new(Q_(conf->params[i].label)), x, x+1, y, y+1,
+                          GTK_SHRINK, GTK_SHRINK, 0, 0 );
+        switch( conf->params[i].type ) {
+        case CONFIG_TYPE_KEY:
+            data->fields[i][0] = text = gtk_entry_new();
+            gtk_entry_set_width_chars( GTK_ENTRY(text), 11 );
+            gtk_entry_set_editable( GTK_ENTRY(text), FALSE );
+            g_signal_connect( text, "key_press_event",
+                              G_CALLBACK(config_key_keypress), NULL );
+            g_signal_connect( text, "button_press_event",
+                              G_CALLBACK(config_key_buttonpress), NULL );
+            g_signal_connect( text, "focus_out_event",
+                              G_CALLBACK(config_key_unfocus), NULL);
+            g_object_set_data( G_OBJECT(text), "keypress_mode", GINT_TO_POINTER(FALSE) );
+            g_object_set_data( G_OBJECT(text), "tag", GINT_TO_POINTER(i) );
+            gtk_table_attach_defaults( GTK_TABLE(table), text, x+1, x+2, y, y+1);
+            focus_chain = g_list_append( focus_chain, text );
+
+            data->fields[i][1] = text2 = gtk_entry_new();
+            gtk_entry_set_width_chars( GTK_ENTRY(text2), 11 );
+            gtk_entry_set_editable( GTK_ENTRY(text2), FALSE );
+            g_signal_connect( text2, "key_press_event",
+                              G_CALLBACK(config_key_keypress), NULL );
+            g_signal_connect( text2, "button_press_event",
+                              G_CALLBACK(config_key_buttonpress), NULL );
+            g_signal_connect( text2, "focus_out_event",
+                              G_CALLBACK(config_key_unfocus), NULL);
+            g_object_set_data( G_OBJECT(text2), "keypress_mode", GINT_TO_POINTER(FALSE) );
+            g_object_set_data( G_OBJECT(text2), "tag", GINT_TO_POINTER(i) );
+            gtk_table_attach_defaults( GTK_TABLE(table), text2, x+2, x+3, y, y+1);
+            focus_chain = g_list_append( focus_chain, text2 );
+
+            if( conf->params[i].value != NULL ) {
+                gchar **parts = g_strsplit(conf->params[i].value,",",3);
+                if( parts[0] != NULL ) {
+                    gtk_entry_set_text( GTK_ENTRY(text), g_strstrip(parts[0]) );
+                    if( parts[1] != NULL ) {
+                        gtk_entry_set_text( GTK_ENTRY(text2), g_strstrip(parts[1]) );
+                    }
+                }
+                g_strfreev(parts);
+            }
+            break;
+        case CONFIG_TYPE_FILE:
+        case CONFIG_TYPE_PATH:
+            data->fields[i][0] = text = gtk_entry_new();
+            data->fields[i][1] = NULL;
+            button = gtk_button_new();
+            gtk_entry_set_text( GTK_ENTRY(text), conf->params[i].value );
+            gtk_entry_set_width_chars( GTK_ENTRY(text), 48 );
+            g_object_set_data( G_OBJECT(text), "tag", GINT_TO_POINTER(i) );
+            gtk_table_attach_defaults( GTK_TABLE(table), text, 1, 2, y, y+1 );
+            gtk_table_attach( GTK_TABLE(table), button, 2, 3, y, y+1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
+            g_signal_connect( text, "changed", G_CALLBACK(config_text_changed), NULL );
+            if( conf->params[i].type == CONFIG_TYPE_FILE ) {
+                GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
+                gtk_button_set_image( GTK_BUTTON(button), image );
+                g_signal_connect( button, "clicked", G_CALLBACK(path_file_button_clicked), text );
+            } else {
+                GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
+                gtk_button_set_image( GTK_BUTTON(button), image );
+                g_signal_connect( button, "clicked", G_CALLBACK(path_dir_button_clicked), text );
+            }
+            break;
+        }
+    }
+    gtk_container_set_focus_chain( GTK_CONTAINER(table), focus_chain );
+//    gtk_gui_run_property_dialog( _("Controller Configuration"), table, controller_config_done );
+    return table;
+}
+
+int gtk_configuration_panel_run( const gchar *title, lxdream_config_group_t group )
+{
+    struct lxdream_config_group tmp;
+    lxdream_clone_config_group( &tmp, group );
+    GtkWidget *panel = gtk_configuration_panel_new( &tmp );
+    int result = gtk_gui_run_property_dialog( title, panel, NULL );
+    if( result == GTK_RESPONSE_ACCEPT ) {
+        lxdream_copy_config_group( group, &tmp );
+        lxdream_save_config();
+    }
+    return result;
+}
</pre></div>
<hr /><a name="file16" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/gtkui</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>gtk_ctrl.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/gtkui/gtk_ctrl.c
+++ lxdream/src/gtkui/gtk_ctrl.c
@@ -34,7 +34,6 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #define LOAD_VMU_TAG ((void *)-1)
 #define CREATE_VMU_TAG ((void *)-2)
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static void controller_device_configure(maple_device_t device);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static void maple_set_device_selection( GtkWidget *combo, maple_device_t device );
 
 struct maple_config_class {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -51,11 +50,6 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > } *maple_slot_data_t;
 
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static struct maple_config_class maple_device_config[] = {
-        { "Sega Controller", controller_device_configure },
-        { "Sega Lightgun", controller_device_configure },
-        { NULL, NULL } };
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static struct maple_slot_data maple_data[MAPLE_MAX_DEVICES];
 
 /**
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -64,174 +58,20 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >  */
 static gboolean maple_device_adjusting = FALSE;
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static void config_keysym_hook( void *data, const gchar *keysym )
-{
-    GtkWidget *widget = (GtkWidget *)data;
-    gtk_entry_set_text( GTK_ENTRY(widget), keysym );
-    g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
-    input_set_keysym_hook(NULL, NULL);
-}
-
-static gboolean config_key_buttonpress( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
-{
-    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
-    if( keypress_mode ) {
-        gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, event->button);
-        if( keysym != NULL ) {
-            config_keysym_hook( widget, keysym );
-            g_free(keysym);
-        }
-        return TRUE;
-    } else {
-        gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
-        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
-        input_set_keysym_hook(config_keysym_hook, widget);
-    }
-    return FALSE;
-}
-
-static gboolean config_key_keypress( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
-{
-    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
-    if( keypress_mode ) {
-        if( event->keyval == GDK_Escape ) {
-            gtk_entry_set_text( GTK_ENTRY(widget), "" );
-            g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
-            return TRUE;
-        }
-        GdkKeymap *keymap = gdk_keymap_get_default();
-        guint keyval;
-
-        gdk_keymap_translate_keyboard_state( keymap, event->hardware_keycode, 0, 0, &keyval, 
-                                             NULL, NULL, NULL );
-        gtk_entry_set_text( GTK_ENTRY(widget), gdk_keyval_name(keyval) );
-        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
-        input_set_keysym_hook(NULL, NULL);
-        return TRUE;
-    } else {
-        switch( event->keyval ) {
-        case GDK_Return:
-        case GDK_KP_Enter:
-            gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
-            g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
-            input_set_keysym_hook(config_keysym_hook, widget);
-            return TRUE;
-        case GDK_BackSpace:
-        case GDK_Delete:
-            gtk_entry_set_text( GTK_ENTRY(widget), "" );
-            return TRUE;
-        }
-        return FALSE;
-    }
-
-}
-
-static void controller_config_done( GtkWidget *panel, gboolean isOK )
-{
-    if( isOK ) {
-        maple_device_t device = (maple_device_t)gtk_object_get_data( GTK_OBJECT(panel), "maple_device" );
-        lxdream_config_entry_t conf = device->get_config(device);
-        int i;
-        for( i=0; conf[i].key != NULL; i++ ) {
-            char buf[64];
-            GtkWidget *entry1, *entry2;
-            const gchar *key1, *key2;
-            snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
-            entry1 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
-            key1 = gtk_entry_get_text(GTK_ENTRY(entry1));
-            snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
-            entry2 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
-            key2 = gtk_entry_get_text(GTK_ENTRY(entry2));
-            if( key1 == NULL || key1[0] == '\0') {
-                lxdream_set_config_value( &conf[i], key2 );
-            } else if( key2 == NULL || key2[0] == '\0') {
-                lxdream_set_config_value( &conf[i], key1 );
-            } else {
-                char buf[64];
-                snprintf( buf, sizeof(buf), "%s, %s", key1, key2 );
-                lxdream_set_config_value( &conf[i], buf );
-            }
-        }
-    }
-    input_set_keysym_hook(NULL, NULL);
-}
-
-static void controller_device_configure( maple_device_t device )
-{
-    lxdream_config_entry_t conf = maple_get_device_config(device);
-    int count, i;
-    for( count=0; conf[count].key != NULL; count++ );
-
-    GtkWidget *table = gtk_table_new( (count+1)>>1, 6, FALSE );
-    GList *focus_chain = NULL;
-    gtk_object_set_data( GTK_OBJECT(table), "maple_device", device );
-    for( i=0; i<count; i++ ) {
-        GtkWidget *text, *text2;
-        char buf[64];
-        int x=0;
-        int y=i;
-        if( i >= (count+1)>>1 ) {
-            x = 3;
-            y -= (count+1)>>1;
-        }
-        gtk_table_attach( GTK_TABLE(table), gtk_label_new(gettext(conf[i].label)), x, x+1, y, y+1, 
-                          GTK_SHRINK, GTK_SHRINK, 0, 0 );
-        text = gtk_entry_new();
-        gtk_entry_set_width_chars( GTK_ENTRY(text), 11 );
-        gtk_entry_set_editable( GTK_ENTRY(text), FALSE );
-        g_signal_connect( text, "key_press_event", 
-                          G_CALLBACK(config_key_keypress), NULL );
-        g_signal_connect( text, "button_press_event",
-                          G_CALLBACK(config_key_buttonpress), NULL );
-        snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
-        g_object_set_data( G_OBJECT(text), "keypress_mode", GINT_TO_POINTER(FALSE) );
-        g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text );
-        gtk_table_attach_defaults( GTK_TABLE(table), text, x+1, x+2, y, y+1);
-        focus_chain = g_list_append( focus_chain, text );
-        text2 = gtk_entry_new();
-        gtk_entry_set_width_chars( GTK_ENTRY(text2), 11 );
-        gtk_entry_set_editable( GTK_ENTRY(text2), FALSE );
-        g_signal_connect( text2, "key_press_event", 
-                          G_CALLBACK(config_key_keypress), NULL );
-        g_signal_connect( text2, "button_press_event",
-                          G_CALLBACK(config_key_buttonpress), NULL );
-        snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
-        g_object_set_data( G_OBJECT(text2), "keypress_mode", GINT_TO_POINTER(FALSE) );
-        g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text2 );
-        gtk_table_attach_defaults( GTK_TABLE(table), text2, x+2, x+3, y, y+1);
-        focus_chain = g_list_append( focus_chain, text2 );
-        if( conf[i].value != NULL ) {
-            gchar **parts = g_strsplit(conf[i].value,",",3);
-            if( parts[0] != NULL ) {
-                gtk_entry_set_text( GTK_ENTRY(text), g_strstrip(parts[0]) );
-                if( parts[1] != NULL ) {
-                    gtk_entry_set_text( GTK_ENTRY(text2), g_strstrip(parts[1]) );
-                }
-            }
-            g_strfreev(parts);
-        }
-    }
-    gtk_container_set_focus_chain( GTK_CONTAINER(table), focus_chain );
-    gtk_gui_run_property_dialog( _("Controller Configuration"), table, controller_config_done );
-}
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static gboolean maple_properties_activated( GtkButton *button, gpointer user_data )
 {
     maple_slot_data_t data = (maple_slot_data_t)user_data;
     if( data->new_device != NULL ) {
         int i;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        for( i=0; maple_device_config[i].name != NULL; i++ ) {
-            if( strcmp(data->new_device->device_class->name, maple_device_config[i].name) == 0 ) {
-                if( data->new_device == data->old_device ) {
-                    // Make a copy at this point if we haven't already
-                    data->new_device = data->old_device->clone(data->old_device);
-                }
-                maple_device_config[i].config_func(data->new_device);
-                break;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        lxdream_config_group_t config = data->new_device->get_config(data->new_device);
+        if( config != NULL ) {
+
+            if( data->new_device == data->old_device ) {
+            // Make a copy at this point if we haven't already
+                data->new_device = data->old_device->clone(data->old_device);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >             }
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        }
-        if( maple_device_config[i].name == NULL ) {
-            gui_error_dialog( _("No configuration page available for device type") );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
+            gtk_configuration_panel_run(_("Controller Configuration"), config);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         }
     }
     return TRUE;
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -310,7 +150,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                         MAPLE_VMU_HAS_NAME(maple_data[i].new_device, vmu_filename) ) {
                     maple_data[i].new_device->destroy(maple_data[i].new_device);
                     maple_data[i].new_device = NULL;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                    gtk_combo_box_set_active(maple_data[i].combo,0);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                    gtk_combo_box_set_active(GTK_COMBO_BOX(maple_data[i].combo),0);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                 }
             }
             MAPLE_SET_VMU_NAME(data->new_device,vmu_filename);
</pre></div>
<hr /><a name="file17" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span id="removed" class="pathname" style="font-family:monospace; float:right; background-color:#ffdddd;" >lxdream/src/gtkui</span><br />
<div id="removed" class="fileheader" style="margin-bottom:.5em; background-color:#ffdddd;" ><big><b>gtk_hotkeys.c</b></big> <small id="info" style="color: #888888;" >removed after 182cfe43c09e</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/gtkui/gtk_hotkeys.c
+++ lxdream/src/gtkui/gtk_hotkeys.c
@@ -1,187 +0,0 @@
</small></pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-/**
- * $Id:  $
- *
- * GTK dialog for defining hotkeys
- *
- * Copyright (c) 2009 wahrhaft.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <string.h>
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-
-#include "lxdream.h"
-#include "display.h"
-#include "gtkui/gtkui.h"
-#include "hotkeys.h"
-
-
-
-static void config_keysym_hook( void *data, const gchar *keysym )
-{
-    GtkWidget *widget = (GtkWidget *)data;
-    gtk_entry_set_text( GTK_ENTRY(widget), keysym );
-    g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
-    input_set_keysym_hook(NULL, NULL);
-}
-
-static gboolean config_key_buttonpress( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
-{
-    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
-    if( keypress_mode ) {
-        gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, event->button);
-        if( keysym != NULL ) {
-            config_keysym_hook( widget, keysym );
-            g_free(keysym);
-        }
-        return TRUE;
-    } else {
-        gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
-        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
-        input_set_keysym_hook(config_keysym_hook, widget);
-    }
-    return FALSE;
-}
-
-static gboolean config_key_keypress( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
-{
-    gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
-    if( keypress_mode ) {
-        if( event->keyval == GDK_Escape ) {
-            gtk_entry_set_text( GTK_ENTRY(widget), "" );
-            g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
-            return TRUE;
-        }
-        GdkKeymap *keymap = gdk_keymap_get_default();
-        guint keyval;
-
-        gdk_keymap_translate_keyboard_state( keymap, event->hardware_keycode, 0, 0, &keyval, 
-                                             NULL, NULL, NULL );
-        gtk_entry_set_text( GTK_ENTRY(widget), gdk_keyval_name(keyval) );
-        g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
-        input_set_keysym_hook(NULL, NULL);
-        return TRUE;
-    } else {
-        switch( event->keyval ) {
-        case GDK_Return:
-        case GDK_KP_Enter:
-            gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
-            g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
-            input_set_keysym_hook(config_keysym_hook, widget);
-            return TRUE;
-        case GDK_BackSpace:
-        case GDK_Delete:
-            gtk_entry_set_text( GTK_ENTRY(widget), "" );
-            return TRUE;
-        }
-        return FALSE;
-    }
-
-}
-
-void hotkeys_dialog_done( GtkWidget *panel, gboolean isOK )
-{
-    if( isOK ) {
-        hotkeys_unregister_keys();
-        lxdream_config_entry_t conf = hotkeys_get_config();
-        int i;
-        for( i=0; conf[i].key != NULL; i++ ) {
-            char buf[64];
-            GtkWidget *entry1, *entry2;
-            const gchar *key1, *key2;
-            snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
-            entry1 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
-            key1 = gtk_entry_get_text(GTK_ENTRY(entry1));
-            snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
-            entry2 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
-            key2 = gtk_entry_get_text(GTK_ENTRY(entry2));
-            if( key1 == NULL || key1[0] == '\0') {
-                lxdream_set_config_value( &conf[i], key2 );
-            } else if( key2 == NULL || key2[0] == '\0') {
-                lxdream_set_config_value( &conf[i], key1 );
-            } else {
-                char buf[64];
-                snprintf( buf, sizeof(buf), "%s, %s", key1, key2 );
-                lxdream_set_config_value( &conf[i], buf );
-            }
-        }
-        lxdream_save_config();
-        hotkeys_register_keys();
-    }
-}
-
-GtkWidget *hotkeys_panel_new()
-{
-    lxdream_config_entry_t conf = hotkeys_get_config();
-    int count, i;
-    for( count=0; conf[count].key != NULL; count++ );
-
-    GtkWidget *table = gtk_table_new( (count+1)>>1, 6, FALSE );
-    GList *focus_chain = NULL;
-    //gtk_object_set_data( GTK_OBJECT(table), "maple_device", device );
-    for( i=0; i<count; i++ ) {
-        GtkWidget *text, *text2;
-        char buf[64];
-        int x=0;
-        int y=i;
-        if( i >= (count+1)>>1 ) {
-            x = 3;
-            y -= (count+1)>>1;
-        }
-        gtk_table_attach( GTK_TABLE(table), gtk_label_new(gettext(conf[i].label)), x, x+1, y, y+1, 
-                          GTK_SHRINK, GTK_SHRINK, 0, 0 );
-        text = gtk_entry_new();
-        gtk_entry_set_width_chars( GTK_ENTRY(text), 11 );
-        gtk_entry_set_editable( GTK_ENTRY(text), FALSE );
-        g_signal_connect( text, "key_press_event", 
-                          G_CALLBACK(config_key_keypress), NULL );
-        g_signal_connect( text, "button_press_event",
-                          G_CALLBACK(config_key_buttonpress), NULL );
-        snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
-        g_object_set_data( G_OBJECT(text), "keypress_mode", GINT_TO_POINTER(FALSE) );
-        g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text );
-        gtk_table_attach_defaults( GTK_TABLE(table), text, x+1, x+2, y, y+1);
-        focus_chain = g_list_append( focus_chain, text );
-        text2 = gtk_entry_new();
-        gtk_entry_set_width_chars( GTK_ENTRY(text2), 11 );
-        gtk_entry_set_editable( GTK_ENTRY(text2), FALSE );
-        g_signal_connect( text2, "key_press_event", 
-                          G_CALLBACK(config_key_keypress), NULL );
-        g_signal_connect( text2, "button_press_event",
-                          G_CALLBACK(config_key_buttonpress), NULL );
-        snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
-        g_object_set_data( G_OBJECT(text2), "keypress_mode", GINT_TO_POINTER(FALSE) );
-        g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text2 );
-        gtk_table_attach_defaults( GTK_TABLE(table), text2, x+2, x+3, y, y+1);
-        focus_chain = g_list_append( focus_chain, text2 );
-        if( conf[i].value != NULL ) {
-            gchar **parts = g_strsplit(conf[i].value,",",3);
-            if( parts[0] != NULL ) {
-                gtk_entry_set_text( GTK_ENTRY(text), g_strstrip(parts[0]) );
-                if( parts[1] != NULL ) {
-                    gtk_entry_set_text( GTK_ENTRY(text2), g_strstrip(parts[1]) );
-                }
-            }
-            g_strfreev(parts);
-        }
-    }
-    gtk_container_set_focus_chain( GTK_CONTAINER(table), focus_chain );
-
-    return table;
-}
-
-void hotkeys_dialog_run( )
-{
-    gtk_gui_run_property_dialog( _("Hotkey Settings"), hotkeys_panel_new(), hotkeys_dialog_done );
-}
</pre></div>
<hr /><a name="file18" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span id="removed" class="pathname" style="font-family:monospace; float:right; background-color:#ffdddd;" >lxdream/src/gtkui</span><br />
<div id="removed" class="fileheader" style="margin-bottom:.5em; background-color:#ffdddd;" ><big><b>gtk_path.c</b></big> <small id="info" style="color: #888888;" >removed after 182cfe43c09e</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/gtkui/gtk_path.c
+++ lxdream/src/gtkui/gtk_path.c
@@ -1,130 +0,0 @@
</small></pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-/**
- * $Id$
- *
- * Define the main (emu) GTK window, along with its menubars,
- * toolbars, etc.
- *
- * Copyright (c) 2005 Nathan Keynes.
- *
- * 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.
- */
-
-#include <assert.h>
-#include <gtk/gtk.h>
-
-#include "lxdream.h"
-#include "dreamcast.h"
-#include "config.h"
-#include "lxpaths.h"
-#include "gtkui/gtkui.h"
-
-static GtkWidget *path_entry[CONFIG_KEY_MAX];
-
-static gboolean path_file_button_clicked( GtkWidget *button, gpointer user_data )
-{
-    GtkWidget *entry = GTK_WIDGET(user_data);
-    GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
-            GTK_FILE_CHOOSER_ACTION_OPEN,
-            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-            NULL );
-    gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
-    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
-    gtk_window_set_modal( GTK_WINDOW(file), TRUE );
-    gtk_widget_show_all( file );
-    gint result = gtk_dialog_run(GTK_DIALOG(file));
-    g_free(filename);
-    if( result == GTK_RESPONSE_ACCEPT ) {
-        filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
-        gtk_entry_set_text(GTK_ENTRY(entry), filename);
-        g_free(filename);
-    }
-    gtk_widget_destroy(file);
-    return TRUE;
-}
-
-static gboolean path_dir_button_clicked( GtkWidget *button, gpointer user_data )
-{
-    GtkWidget *entry = GTK_WIDGET(user_data);
-    GtkWidget *file = gtk_file_chooser_dialog_new( _("Select file"), NULL,
-            GTK_FILE_CHOOSER_ACTION_OPEN,
-            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-            NULL );
-    gchar *filename = get_expanded_path(gtk_entry_get_text(GTK_ENTRY(entry)));
-    gtk_file_chooser_set_action( GTK_FILE_CHOOSER(file), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
-    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(file), filename );
-    gtk_window_set_modal( GTK_WINDOW(file), TRUE );
-    gtk_widget_show_all( file );
-    gint result = gtk_dialog_run(GTK_DIALOG(file));
-    g_free(filename);
-    if( result == GTK_RESPONSE_ACCEPT ) {
-        filename = get_escaped_path(gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(file) ));
-        gtk_entry_set_text(GTK_ENTRY(entry), filename);
-        g_free(filename);
-    }
-    gtk_widget_destroy(file);
-    return TRUE;
-}
-
-GtkWidget *path_panel_new(void)
-{
-    int i, y=0;
-    GtkWidget *table = gtk_table_new( CONFIG_KEY_MAX, 3, FALSE );
-    for( i=0; i<CONFIG_KEY_MAX; i++ ) {
-        const struct lxdream_config_entry *entry = lxdream_get_global_config_entry(i);
-        if( entry->label != NULL ) {
-            GtkWidget *text = path_entry[i] = gtk_entry_new();
-            GtkWidget *button = gtk_button_new();
-            gtk_table_attach( GTK_TABLE(table), gtk_label_new(Q_(entry->label)), 0, 1, y, y+1,
-                              GTK_SHRINK, GTK_SHRINK, 0, 0);
-            gtk_entry_set_text( GTK_ENTRY(text), lxdream_get_global_config_value(i) );
-            gtk_entry_set_width_chars( GTK_ENTRY(text), 48 );
-            gtk_table_attach_defaults( GTK_TABLE(table), text, 1, 2, y, y+1 );
-            gtk_table_attach( GTK_TABLE(table), button, 2, 3, y, y+1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
-            if( entry->type == CONFIG_TYPE_FILE ) {
-                GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
-                gtk_button_set_image( GTK_BUTTON(button), image );
-                g_signal_connect( button, "clicked", G_CALLBACK(path_file_button_clicked), text );
-            } else {
-                GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
-                gtk_button_set_image( GTK_BUTTON(button), image );
-                g_signal_connect( button, "clicked", G_CALLBACK(path_dir_button_clicked), text );
-            }
-            y++;
-        }
-    }
-    gtk_table_resize( GTK_TABLE(table), y, 3 );
-    return table;
-
-}
-
-void path_panel_done( GtkWidget *panel, gboolean isOK )
-{
-    if( isOK ) {
-        int i;
-        for(i=0; i<CONFIG_KEY_MAX; i++ ) {
-            if( path_entry[i] != NULL ) {
-                const char *filename = gtk_entry_get_text( GTK_ENTRY(path_entry[i]) );
-                lxdream_set_global_config_value( i, filename );
-            }
-        }
-
-        lxdream_save_config();
-        dreamcast_config_changed();
-        gtk_gui_update();
-    }
-}
-
-void path_dialog_run( void )
-{
-    gtk_gui_run_property_dialog( _("Path Settings"), path_panel_new(), path_panel_done );
-}
</pre></div>
<hr /><a name="file19" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/gtkui</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>gtkcb.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/gtkui/gtkcb.c
+++ lxdream/src/gtkui/gtkcb.c
@@ -79,7 +79,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                          int initial_dir_key )
 {
     GtkWidget *file;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    gchar *filename;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    gchar *filename<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" > = NULL</span>;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     
     file = gtk_file_chooser_dialog_new( title, NULL,
             GTK_FILE_CHOOSER_ACTION_SAVE,
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -257,7 +257,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 void path_settings_callback( GtkAction *action, gpointer user_data)
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    path_dialog_run();
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    gtk_configuration_panel_run( _("Path Settings"), lxdream_get_config_group(CONFIG_GROUP_GLOBAL) );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 void audio_settings_callback( GtkAction *action, gpointer user_data)
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -279,7 +279,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 void hotkey_settings_callback( GtkAction *action, gpointer user_data)
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    hotkeys_dialog_run();
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    gtk_configuration_panel_run( _("Hotkey Settings"), lxdream_get_config_group(CONFIG_GROUP_HOTKEYS) );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 void fullscreen_toggle_callback( GtkToggleAction *action, gpointer user_data)
</pre></div>
<hr /><a name="file20" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/gtkui</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>gtkui.h</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/gtkui/gtkui.h
+++ lxdream/src/gtkui/gtkui.h
@@ -36,6 +36,8 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > typedef struct mmio_window_info *mmio_window_t;
 typedef struct dump_window_info *dump_window_t;
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+struct lxdream_config_group; /* Forward declaration */
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /**
  * Construct and show the main window, returning an 
  * opaque pointer to the window.
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -85,7 +87,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > typedef void (*gtk_dialog_done_fn)(GtkWidget *panel, gboolean isOK);
 void gtk_gui_enable_action( const gchar *action, gboolean enabled );
 gint gtk_gui_run_property_dialog( const gchar *title, GtkWidget *panel, gtk_dialog_done_fn fn );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+int gtk_configuration_panel_run( const gchar *title, struct lxdream_config_group *group );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 typedef gboolean (*file_callback_t)( const gchar *filename );
 gchar *open_file_dialog( const char *title, const char *pattern, const char *patname,
</pre></div>
<hr /><a name="file21" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>hotkeys.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/hotkeys.c
+++ lxdream/src/hotkeys.c
@@ -1,5 +1,5 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /**
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >- * $Id<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >:  </span>$
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+ * $Id$
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >  *
  * Handles hotkeys for pause/continue, save states, quit, etc
  *
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -27,34 +27,37 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #include "gui.h"
 #include "config.h"
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static void hotkey_resume_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
-static void hotkey_stop_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
-static void hotkey_reset_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
-static void hotkey_exit_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
-static void hotkey_state_select_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
-static void hotkey_state_save_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
-static void hotkey_state_load_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static void hotkey_key_callback( void *data, uint32_t value, uint32_t pressure, gboolean isKeyDown );
+static gboolean hotkey_config_changed( void *data, lxdream_config_group_t group, unsigned key,
+                                       const gchar *oldval, const gchar *newval );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#define TAG_RESUME 0
+#define TAG_STOP 1
+#define TAG_RESET 2
+#define TAG_EXIT 3
+#define TAG_SAVE 4
+#define TAG_LOAD 5
+#define TAG_SELECT(i) (6+(i))
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-struct lxdream_config_entry hotkeys_config[] = {
-        {"resume", N_("Resume emulation"), CONFIG_TYPE_KEY},
-        {"stop", N_("Stop emulation"), CONFIG_TYPE_KEY},
-        {"reset", N_("Reset emulator"), CONFIG_TYPE_KEY},
-        {"exit", N_("Exit emulator"), CONFIG_TYPE_KEY},
-        {"save", N_("Save current quick save"), CONFIG_TYPE_KEY},
-        {"load", N_("Load current quick save"), CONFIG_TYPE_KEY},
-        {"state0", N_("Select quick save state 0"), CONFIG_TYPE_KEY},
-        {"state1", N_("Select quick save state 1"), CONFIG_TYPE_KEY},
-        {"state2", N_("Select quick save state 2"), CONFIG_TYPE_KEY},
-        {"state3", N_("Select quick save state 3"), CONFIG_TYPE_KEY},
-        {"state4", N_("Select quick save state 4"), CONFIG_TYPE_KEY},
-        {"state5", N_("Select quick save state 5"), CONFIG_TYPE_KEY},
-        {"state6", N_("Select quick save state 6"), CONFIG_TYPE_KEY},
-        {"state7", N_("Select quick save state 7"), CONFIG_TYPE_KEY},
-        {"state8", N_("Select quick save state 8"), CONFIG_TYPE_KEY},
-        {"state9", N_("Select quick save state 9"), CONFIG_TYPE_KEY},
-        {NULL, CONFIG_TYPE_NONE}
-};
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+struct lxdream_config_group hotkeys_group = {
+    "hotkeys", input_keygroup_changed, hotkey_key_callback, NULL, {
+        {"resume", N_("Resume emulation"), CONFIG_TYPE_KEY, NULL, TAG_RESUME },
+        {"stop", N_("Stop emulation"), CONFIG_TYPE_KEY, NULL, TAG_STOP },
+        {"reset", N_("Reset emulator"), CONFIG_TYPE_KEY, NULL, TAG_RESET },
+        {"exit", N_("Exit emulator"), CONFIG_TYPE_KEY, NULL, TAG_EXIT },
+        {"save", N_("Save current quick save"), CONFIG_TYPE_KEY, NULL, TAG_SAVE },
+        {"load", N_("Load current quick save"), CONFIG_TYPE_KEY, NULL, TAG_LOAD },
+        {"state0", N_("Select quick save state 0"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(0) },
+        {"state1", N_("Select quick save state 1"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(1) },
+        {"state2", N_("Select quick save state 2"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(2) },
+        {"state3", N_("Select quick save state 3"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(3) },
+        {"state4", N_("Select quick save state 4"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(4) },
+        {"state5", N_("Select quick save state 5"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(5) },
+        {"state6", N_("Select quick save state 6"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(6) },
+        {"state7", N_("Select quick save state 7"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(7) },
+        {"state8", N_("Select quick save state 8"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(8) },
+        {"state9", N_("Select quick save state 9"), CONFIG_TYPE_KEY, NULL, TAG_SELECT(9) },
+        {NULL, CONFIG_TYPE_NONE}} };
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 void hotkeys_init() 
 {
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -63,86 +66,47 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 void hotkeys_register_keys()
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    input_register_key(hotkeys_config[0].value, &hotkey_resume_callback, NULL, 0);
-    input_register_key(hotkeys_config[1].value, &hotkey_stop_callback, NULL, 0);
-    input_register_key(hotkeys_config[2].value, &hotkey_reset_callback, NULL, 0);
-    input_register_key(hotkeys_config[3].value, &hotkey_exit_callback, NULL, 0);
-    input_register_key(hotkeys_config[4].value, &hotkey_state_save_callback, NULL, 0);
-    input_register_key(hotkeys_config[5].value, &hotkey_state_load_callback, NULL, 0);
-    for (int i = 0; i < 10; i++)
-    {
-        input_register_key(hotkeys_config[6 + i].value, &hotkey_state_select_callback, NULL, i);
-    }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    input_register_keygroup( &hotkeys_group );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 void hotkeys_unregister_keys()
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    input_unregister_key(hotkeys_config[0].value, &hotkey_resume_callback, NULL, 0);
-    input_unregister_key(hotkeys_config[1].value, &hotkey_stop_callback, NULL, 0);
-    input_unregister_key(hotkeys_config[2].value, &hotkey_reset_callback, NULL, 0);
-    input_unregister_key(hotkeys_config[3].value, &hotkey_exit_callback, NULL, 0);
-    input_unregister_key(hotkeys_config[4].value, &hotkey_state_save_callback, NULL, 0);
-    input_unregister_key(hotkeys_config[5].value, &hotkey_state_load_callback, NULL, 0);
-    for (int i = 0; i < 10; i++)
-    {
-        input_unregister_key(hotkeys_config[6 + i].value, &hotkey_state_select_callback, NULL, i);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    input_unregister_keygroup( &hotkeys_group );
+}
+
+lxdream_config_group_t hotkeys_get_config()
+{
+    return &hotkeys_group;
+}
+
+static void hotkey_key_callback( void *data, uint32_t value, uint32_t pressure, gboolean isKeyDown )
+{
+    if( isKeyDown ) {
+        switch(value) {
+        case TAG_RESUME:
+            if( !dreamcast_is_running() )
+                gui_do_later(dreamcast_run);
+            break;
+        case TAG_STOP:
+            if( dreamcast_is_running() )
+                gui_do_later(dreamcast_stop);
+            break;
+        case TAG_RESET:
+            dreamcast_reset();
+            break;
+        case TAG_EXIT:
+            dreamcast_shutdown();
+            exit(0);
+            break;
+        case TAG_SAVE:
+            dreamcast_quick_save();
+            break;
+        case TAG_LOAD:
+            dreamcast_quick_load();
+            break;
+        default:
+            dreamcast_set_quick_state(value- TAG_SELECT(0) );
+            break;
+        }
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
 }
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-
-lxdream_config_entry_t hotkeys_get_config()
-{
-    return hotkeys_config;
-}
-
-
-static void hotkey_resume_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown )
-{
-    if (isKeyDown && !dreamcast_is_running() ) {
-        gui_do_later(dreamcast_run);
-    }
-}
-
-static void hotkey_stop_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown )
-{
-    if (isKeyDown && dreamcast_is_running() ) {
-        gui_do_later(dreamcast_stop);
-    }
-}
-
-static void hotkey_reset_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown )
-{
-    if (isKeyDown) {
-        dreamcast_reset();
-    }
-}
-
-static void hotkey_exit_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown )
-{
-    if (isKeyDown) {
-        dreamcast_shutdown();
-    }
-    exit(0);
-}
-
-static void hotkey_state_select_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown )
-{
-    if (isKeyDown) {
-        dreamcast_set_quick_state(value);
-    }
-}
-
-static void hotkey_state_save_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown )
-{
-    if (isKeyDown) {
-        dreamcast_quick_save();
-    }
-}
-
-static void hotkey_state_load_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown )
-{
-    if (isKeyDown) {
-        dreamcast_quick_load();
-    }
-}
-
-
</pre></div>
<hr /><a name="file22" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>hotkeys.h</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/hotkeys.h
+++ lxdream/src/hotkeys.h
@@ -1,5 +1,5 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /**
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >- * $Id<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >:  </span>$
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+ * $Id$
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >  *
  * Handles hotkeys for pause/continue, save states, quit, etc
  *
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -26,7 +26,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #endif
 
         void hotkeys_init();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-        lxdream_config_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >entry</span>_t hotkeys_get_config();
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+        lxdream_config_<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >group</span>_t hotkeys_get_config();
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         void hotkeys_register_keys();
         void hotkeys_unregister_keys();
 
</pre></div>
<hr /><a name="file23" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/maple</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>controller.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/maple/controller.c
+++ lxdream/src/maple/controller.c
@@ -79,18 +79,17 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 static void controller_attach( maple_device_t dev );
 static void controller_detach( maple_device_t dev );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static void controller_destroy( maple_device_t dev );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static void controller_key_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static maple_device_t controller_clone( maple_device_t dev );
 static maple_device_t controller_new();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static lxdream_config_entry_t controller_get_config( maple_device_t dev );
-static void controller_set_config_value( maple_device_t dev, unsigned int key, const gchar *value );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static lxdream_config_group_t controller_get_config( maple_device_t dev );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static int controller_get_cond( maple_device_t dev, int function, unsigned char *outbuf,
                          unsigned int *outlen );
 
 typedef struct controller_device {
     struct maple_device dev;
     uint32_t condition[2];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    struct lxdream_config_entry config[CONTROLLER_CONFIG_ENTRIES+1];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    struct lxdream_config_group config;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > } *controller_device_t;
 
 struct maple_device_class controller_class = { "Sega Controller",
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -99,26 +98,30 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static struct controller_device base_controller = {
         { MAPLE_DEVICE_TAG, &controller_class,
           CONTROLLER_IDENT, CONTROLLER_VERSION, 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-          controller_get_config, controller_set_config_value, 
-          controller_attach, controller_detach, controller_destroy,
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+          controller_get_config,
+          controller_attach, controller_detach, maple_default_destroy,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >           controller_clone, NULL, NULL, controller_get_cond, NULL, NULL, NULL, NULL, NULL, NULL },
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-          {0x0000FFFF, 0x80808080}, 
-          {{ "dpad left", N_("Dpad left"), CONFIG_TYPE_KEY },
-           { "dpad right", N_("Dpad right"), CONFIG_TYPE_KEY },
-           { "dpad up", N_("Dpad up"), CONFIG_TYPE_KEY },
-           { "dpad down", N_("Dpad down"), CONFIG_TYPE_KEY },
-           { "analog left", N_("Analog left"), CONFIG_TYPE_KEY },
-           { "analog right", N_("Analog right"), CONFIG_TYPE_KEY },
-           { "analog up", N_("Analog up"), CONFIG_TYPE_KEY },
-           { "analog down", N_("Analog down"), CONFIG_TYPE_KEY },
-           { "button X", N_("Button X"), CONFIG_TYPE_KEY },
-           { "button Y", N_("Button Y"), CONFIG_TYPE_KEY },
-           { "button A", N_("Button A"), CONFIG_TYPE_KEY },
-           { "button B", N_("Button B"), CONFIG_TYPE_KEY },
-           { "trigger left", N_("Trigger left"), CONFIG_TYPE_KEY },
-           { "trigger right", N_("Trigger right"), CONFIG_TYPE_KEY },
-           { "start", N_("Start button"), CONFIG_TYPE_KEY },
-           { NULL, CONFIG_TYPE_NONE }} };
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+          {0x0000FFFF, 0x80808080},
+          {"Sega Controller", NULL, controller_key_callback, NULL,
+           {{ "dpad left", N_("Dpad left"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_LEFT },
+            { "dpad right", N_("Dpad right"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_RIGHT },
+            { "dpad up", N_("Dpad up"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_UP },
+            { "dpad down", N_("Dpad down"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_DOWN },
+            { "analog left", N_("Analog left"), CONFIG_TYPE_KEY, NULL, JOY_LEFT },
+            { "analog right", N_("Analog right"), CONFIG_TYPE_KEY, NULL, JOY_RIGHT },
+            { "analog up", N_("Analog up"), CONFIG_TYPE_KEY, NULL, JOY_UP },
+            { "analog down", N_("Analog down"), CONFIG_TYPE_KEY, NULL, JOY_DOWN },
+            { "button X", N_("Button X"), CONFIG_TYPE_KEY, NULL, BUTTON_X },
+            { "button Y", N_("Button Y"), CONFIG_TYPE_KEY, NULL, BUTTON_Y },
+            { "button A", N_("Button A"), CONFIG_TYPE_KEY, NULL, BUTTON_A },
+            { "button B", N_("Button B"), CONFIG_TYPE_KEY, NULL, BUTTON_B },
+            { "trigger left", N_("Trigger left"), CONFIG_TYPE_KEY, NULL, BUTTON_LEFT_TRIGGER },
+            { "trigger right", N_("Trigger right"), CONFIG_TYPE_KEY, NULL, BUTTON_RIGHT_TRIGGER },
+            { "start", N_("Start button"), CONFIG_TYPE_KEY, NULL, BUTTON_START },
+            { NULL, CONFIG_TYPE_NONE }}}  };
+
+/* Get the controller_device * from a lxdream_config_group_t */
+#define DEV_FROM_CONFIG_GROUP(grp)  ((controller_device_t)(((char *)grp) - offsetof( struct controller_device, config )))
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 static int config_button_map[] = { 
         BUTTON_DPAD_LEFT, BUTTON_DPAD_RIGHT, BUTTON_DPAD_UP, BUTTON_DPAD_DOWN,
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -129,8 +132,9 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 static maple_device_t controller_new( )
 {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    controller_device_t dev = malloc( sizeof(<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >struct controller_device</span>) );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    controller_device_t dev = malloc( sizeof(<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >base_controller</span>) );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     memcpy( dev, &base_controller, sizeof(base_controller) );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    dev->config.data = dev;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     return MAPLE_DEVICE(dev);
 }
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -138,7 +142,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     controller_device_t src = (controller_device_t)srcdevice;
     controller_device_t dev = (controller_device_t)controller_new();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    lxdream_copy_config_list( dev->config, src->config );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    lxdream_copy_config_group( &dev->config, &src->config );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     memcpy( dev->condition, src->condition, sizeof(src->condition) );
     return MAPLE_DEVICE(dev);
 }
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -194,25 +198,10 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static lxdream_config_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >entry</span>_t controller_get_config( maple_device_t mdev )
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static lxdream_config_<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >group</span>_t controller_get_config( maple_device_t mdev )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     controller_device_t dev = (controller_device_t)mdev;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    return dev->config;
-}
-
-static void controller_set_config_value( maple_device_t mdev, unsigned int key, const gchar *value )
-{
-    controller_device_t dev = (controller_device_t)mdev;
-    assert( key < CONTROLLER_CONFIG_ENTRIES );
-    
-    input_unregister_key( dev->config[key].value, controller_key_callback, dev, config_button_map[key] );
-    lxdream_set_config_value( &dev->config[key], value );
-    input_register_key( dev->config[key].value, controller_key_callback, dev, config_button_map[key] );
-}
-
-static void controller_destroy( maple_device_t mdev )
-{
-    free( mdev );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return &dev->config;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 /**
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -222,20 +211,15 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static void controller_attach( maple_device_t mdev )
 {
     controller_device_t dev = (controller_device_t)mdev;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    int i;
-    for( i=0; i<CONTROLLER_CONFIG_ENTRIES; i++ ) {
-        input_register_key( dev->config[i].value, controller_key_callback, dev, config_button_map[i] );
-    }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    dev->config.on_change = input_keygroup_changed;
+    input_register_keygroup( &dev->config );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 static void controller_detach( maple_device_t mdev )
 {
     controller_device_t dev = (controller_device_t)mdev;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    int i;
-    for( i=0; i<CONTROLLER_CONFIG_ENTRIES; i++ ) {
-        input_unregister_key( dev->config[i].value, controller_key_callback, dev, config_button_map[i] );
-    }
-
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    input_unregister_keygroup( &dev->config );
+    dev->config.on_change = NULL;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 
</pre></div>
<hr /><a name="file24" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/maple</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>kbd.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/maple/kbd.c
+++ lxdream/src/maple/kbd.c
@@ -57,7 +57,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static struct keyboard_device base_keyboard = {
         { MAPLE_DEVICE_TAG, &keyboard_class,
                 KEYBOARD_IDENT, KEYBOARD_VERSION, 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                NULL, <span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >NULL, </span>keyboard_attach, keyboard_detach, maple_default_destroy,
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                NULL, keyboard_attach, keyboard_detach, maple_default_destroy,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                 keyboard_clone, NULL, NULL, keyboard_get_cond, NULL, NULL, NULL,
                 NULL, NULL, NULL},
                 {0,0,0,0,0,0,0,0}, 
</pre></div>
<hr /><a name="file25" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/maple</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>lightgun.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/maple/lightgun.c
+++ lxdream/src/maple/lightgun.c
@@ -57,19 +57,22 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static void lightgun_destroy( maple_device_t dev );
 static maple_device_t lightgun_clone( maple_device_t dev );
 static maple_device_t lightgun_new();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static lxdream_config_entry_t lightgun_get_config( maple_device_t dev );
-static void lightgun_set_config_value( maple_device_t dev, unsigned int key, const gchar *value );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static void lightgun_key_callback( void *mdev, uint32_t value, uint32_t pressure, gboolean isKeyDown );
+static lxdream_config_group_t lightgun_get_config( maple_device_t dev );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static int lightgun_get_cond( maple_device_t dev, int function, unsigned char *outbuf,
                          unsigned int *outlen );
 static void lightgun_start_gun( maple_device_t dev );
 static void lightgun_stop_gun( maple_device_t dev );
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static gboolean lightgun_set_config_value( lxdream_config_group_t group, unsigned int key,
+                                           const gchar *oldvalue, const gchar *value );
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > typedef struct lightgun_device {
     struct maple_device dev;
     uint32_t condition[2];
     int gun_active;
     int mouse_x, mouse_y;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    struct lxdream_config_entry config[LIGHTGUN_CONFIG_ENTRIES+1];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    struct lxdream_config_group config;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > } *lightgun_device_t;
 
 struct maple_device_class lightgun_class = { "Sega Lightgun", 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -78,30 +81,29 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static struct lightgun_device base_lightgun = {
         { MAPLE_DEVICE_TAG, &lightgun_class,
           LIGHTGUN_IDENT, LIGHTGUN_VERSION, 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-          lightgun_get_config, lightgun_set_config_value, 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+          lightgun_get_config,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >           lightgun_attach, lightgun_detach, lightgun_destroy,
           lightgun_clone, NULL, NULL, lightgun_get_cond, NULL, NULL, NULL, NULL,
           lightgun_start_gun, lightgun_stop_gun},
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-          {0x0000FFFF, 0x80808080}, 0, -1, -1, 
-          {{ "dpad left", N_("Dpad left"), CONFIG_TYPE_KEY },
-           { "dpad right", N_("Dpad right"), CONFIG_TYPE_KEY },
-           { "dpad up", N_("Dpad up"), CONFIG_TYPE_KEY },
-           { "dpad down", N_("Dpad down"), CONFIG_TYPE_KEY },
-           { "button A", N_("Button A"), CONFIG_TYPE_KEY },
-           { "button B", N_("Button B"), CONFIG_TYPE_KEY },
-           { "start", N_("Start button"), CONFIG_TYPE_KEY },
-           { NULL, CONFIG_TYPE_NONE }} };
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+          {0x0000FFFF, 0x80808080}, 0, -1, -1,
+          {"Sega Lightgun", NULL, lightgun_key_callback, NULL,
+           {{ "dpad left", N_("Dpad left"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_LEFT },
+            { "dpad right", N_("Dpad right"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_RIGHT },
+            { "dpad up", N_("Dpad up"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_UP },
+            { "dpad down", N_("Dpad down"), CONFIG_TYPE_KEY, NULL, BUTTON_DPAD_DOWN },
+            { "button A", N_("Button A"), CONFIG_TYPE_KEY, NULL, BUTTON_A },
+            { "button B", N_("Button B"), CONFIG_TYPE_KEY, NULL, BUTTON_B },
+            { "start", N_("Start button"), CONFIG_TYPE_KEY, NULL, BUTTON_START },
+            { NULL, CONFIG_TYPE_NONE }}}  };
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static int config_button_map[] = { 
-        BUTTON_DPAD_LEFT, BUTTON_DPAD_RIGHT, BUTTON_DPAD_UP, BUTTON_DPAD_DOWN,
-        BUTTON_A, BUTTON_B, BUTTON_START };
-        
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #define lightgun(x) ((lightgun_device_t)(x))
 
 static maple_device_t lightgun_new( )
 {
     lightgun_device_t dev = malloc( sizeof(struct lightgun_device) );
     memcpy( dev, &base_lightgun, sizeof(base_lightgun) );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    dev->config.data = dev;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     return MAPLE_DEVICE(dev);
 }
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -109,11 +111,22 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     lightgun_device_t src = (lightgun_device_t)srcdevice;
     lightgun_device_t dev = (lightgun_device_t)lightgun_new();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    lxdream_copy_config_list( dev->config, src->config );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    lxdream_copy_config_group( &dev->config, &src->config );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     memcpy( dev->condition, src->condition, sizeof(src->condition) );
     return MAPLE_DEVICE(dev);
 }
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static lxdream_config_group_t lightgun_get_config( maple_device_t mdev )
+{
+    lightgun_device_t dev = (lightgun_device_t)mdev;
+    return &dev->config;
+}
+
+static void lightgun_destroy( maple_device_t mdev )
+{
+    free( mdev );
+}
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /**
  * Input callback 
  */
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -127,28 +140,6 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     }
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static lxdream_config_entry_t lightgun_get_config( maple_device_t mdev )
-{
-    lightgun_device_t dev = (lightgun_device_t)mdev;
-    return dev->config;
-}
-
-static void lightgun_set_config_value( maple_device_t mdev, unsigned int key, const gchar *value )
-{
-    lightgun_device_t dev = (lightgun_device_t)mdev;
-    assert( key < LIGHTGUN_CONFIG_ENTRIES );
-    
-    input_unregister_key( dev->config[key].value, lightgun_key_callback, dev, config_button_map[key] );
-    lxdream_set_config_value( &dev->config[key], value );
-    input_register_key( dev->config[key].value, lightgun_key_callback, dev, config_button_map[key] );
-}
-
-static void lightgun_destroy( maple_device_t mdev )
-{
-    free( mdev );
-}
-
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static void lightgun_mouse_callback( void *mdev, uint32_t buttons, int32_t x, int32_t y, gboolean absolute )
 {
     lightgun_device_t dev = (lightgun_device_t)mdev;
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -169,21 +160,16 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static void lightgun_attach( maple_device_t mdev )
 {
     lightgun_device_t dev = (lightgun_device_t)mdev;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    int i;
-    for( i=0; i<LIGHTGUN_CONFIG_ENTRIES; i++ ) {
-        input_register_key( dev->config[i].value, lightgun_key_callback, dev, config_button_map[i] );
-    }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    dev->config.on_change = input_keygroup_changed;
+    input_register_keygroup( &dev->config );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     input_register_mouse_hook( TRUE, lightgun_mouse_callback, dev );
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 static void lightgun_detach( maple_device_t mdev )
 {
     lightgun_device_t dev = (lightgun_device_t)mdev;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    int i;
-    for( i=0; i<LIGHTGUN_CONFIG_ENTRIES; i++ ) {
-        input_unregister_key( dev->config[i].value, lightgun_key_callback, dev, config_button_map[i] );
-    }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    input_unregister_keygroup( &dev->config );
+    dev->config.on_change = NULL;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     input_unregister_mouse_hook( lightgun_mouse_callback, dev );
 
 }
</pre></div>
<hr /><a name="file26" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/maple</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>maple.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/maple/maple.c
+++ lxdream/src/maple/maple.c
@@ -61,7 +61,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     return (const struct maple_device_class **)maple_device_classes;
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-lxdream_config_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >entry</span>_t maple_get_device_config( maple_device_t dev )
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+lxdream_config_<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >group</span>_t maple_get_device_config( maple_device_t dev )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     if( dev->get_config == NULL )
         return NULL;
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -397,13 +397,6 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     return mode == MAPLE_GRAB_YES;
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-void maple_set_device_config_value( maple_device_t dev, unsigned int key, const gchar *value )
-{
-    if( dev != NULL && dev->set_config_value != NULL ) {
-        dev->set_config_value( dev, key, value );
-    }
-}
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > void maple_default_destroy( maple_device_t mdev )
 {
     free(mdev);
</pre></div>
<hr /><a name="file27" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/maple</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>maple.h</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/maple/maple.h
+++ lxdream/src/maple/maple.h
@@ -86,8 +86,8 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /* Some convenience methods for VMU handling */
 #define MAPLE_IS_VMU(dev)  ((dev)->device_class == &vmu_class)
 #define MAPLE_IS_VMU_CLASS(clz)  ((clz) == &vmu_class)
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-#define MAPLE_VMU_NAME(dev) (((dev)->get_config(dev))[0].value)
-#define MAPLE_SET_VMU_NAME(dev,name) ((dev)->set_config_value((dev),0,(name)))
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#define MAPLE_VMU_NAME(dev) (((dev)->get_config(dev))->params[0].value)
+#define MAPLE_SET_VMU_NAME(dev,name) lxdream_set_config_value( (dev)->get_config(dev), 0 ,(name) )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > #define MAPLE_VMU_HAS_NAME(d1,name) (MAPLE_VMU_NAME(d1) == NULL ? name == NULL : \
     name != NULL && strcmp(MAPLE_VMU_NAME(d1),name) == 0)
 #define MAPLE_IS_SAME_VMU(d1,d2) (MAPLE_VMU_NAME(d1) == NULL ? MAPLE_VMU_NAME(d2) == NULL : \
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -99,6 +99,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > struct maple_device_class {
     const char *name;
     int flags;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     maple_device_t (*new_device)();
 };
 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -110,8 +111,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     maple_device_class_t device_class;
     unsigned char ident[112];
     unsigned char version[80];
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    lxdream_config_entry_t (*get_config)(struct maple_device *dev);
-    void (*set_config_value)(struct maple_device *dev, unsigned int key, const gchar *value);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    lxdream_config_group_t (*get_config)(struct maple_device *dev);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     void (*attach)(struct maple_device *dev);
     void (*detach)(struct maple_device *dev);
     void (*destroy)(struct maple_device *dev);
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -142,8 +142,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > maple_device_t maple_get_device( unsigned int port, unsigned int periph );
 const struct maple_device_class *maple_get_device_class( const gchar *name );
 const struct maple_device_class **maple_get_device_classes();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-lxdream_config_entry_t maple_get_device_config( maple_device_t dev );
-void maple_set_device_config_value( maple_device_t dev, unsigned int key, const gchar *value );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+lxdream_config_group_t maple_get_device_config( maple_device_t dev );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 void maple_handle_buffer( uint32_t buffer );
 void maple_attach_device( maple_device_t dev, unsigned int port, unsigned int periph );
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -152,6 +151,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > void maple_reattach_all( );
 gboolean maple_should_grab();
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > /**
  * Default destroy implementation that just frees the dev memory.
  */
</pre></div>
<hr /><a name="file28" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/maple</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>mouse.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/maple/mouse.c
+++ lxdream/src/maple/mouse.c
@@ -60,7 +60,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static struct mouse_device base_mouse = {
         { MAPLE_DEVICE_TAG, &mouse_class,
                 MOUSE_IDENT, MOUSE_VERSION, 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-                NULL, <span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >NULL, </span>mouse_attach, mouse_detach, maple_default_destroy,
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+                NULL, mouse_attach, mouse_detach, maple_default_destroy,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >                 mouse_clone, NULL, NULL, mouse_get_cond, NULL, NULL, NULL, NULL, NULL, NULL },
                 0, {0,0,0,0,0,0,0,0}, 
 };
</pre></div>
<hr /><a name="file29" /><div class="file" style="border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;" >
<span class="pathname" style="font-family:monospace; float:right;" >lxdream/src/maple</span><br />
<div class="fileheader" style="margin-bottom:.5em;" ><big><b>vmu.c</b></big> <small id="info" style="color: #888888;" >182cfe43c09e -> d82e04e6d497</small></div>
<pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >--- lxdream/src/maple/vmu.c
+++ lxdream/src/maple/vmu.c
@@ -57,8 +57,9 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static void vmu_destroy( maple_device_t dev );
 static maple_device_t vmu_clone( maple_device_t dev );
 static maple_device_t vmu_new();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static lxdream_config_entry_t vmu_get_config( maple_device_t dev );
-static void vmu_set_config_value( maple_device_t dev, unsigned int key, const gchar *value );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static lxdream_config_group_t vmu_get_config( maple_device_t dev );
+static gboolean vmu_set_config_value( void *data, lxdream_config_group_t group, unsigned int key,
+                                      const gchar *oldvalue, const gchar *value );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static int vmu_get_condition( maple_device_t dev, int function, unsigned char *outbuf,
                               unsigned int *outlen );
 static int vmu_get_meminfo( maple_device_t dev, int function, unsigned int pt, 
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -76,25 +77,31 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     struct maple_device dev;
     vmu_volume_t vol;
     char lcd_bitmap[VMU_LCD_SIZE]; /* 48x32 bitmap */
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    struct lxdream_config_entry config[VMU_CONFIG_ENTRIES+1];
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    struct lxdream_config_group config;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > } *vmu_device_t;
 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+#define DEV_FROM_CONFIG_GROUP(grp)  ((vmu_device_t)(((char *)grp) - offsetof( struct vmu_device, config )))
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > struct maple_device_class vmu_class = { "Sega VMU", MAPLE_GRAB_DONTCARE, vmu_new };
 
 static struct vmu_device base_vmu = {
         { MAPLE_DEVICE_TAG, &vmu_class,
           VMU_IDENT, VMU_VERSION, 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-          vmu_get_config, vmu_set_config_value, 
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+          vmu_get_config,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >           vmu_attach, vmu_detach, vmu_destroy,
           vmu_clone, NULL, NULL, vmu_get_condition, NULL,
           vmu_get_meminfo, vmu_read_block, vmu_write_block, NULL, NULL },
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-          NULL, {0},<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" > </span>
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+          NULL, {0},
+          {"Sega VMU", vmu_set_config_value, NULL, NULL,
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >           {{ "volume", N_("Volume"), CONFIG_TYPE_FILE },
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-           { NULL, CONFIG_TYPE_NONE }} };
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+           { NULL, CONFIG_TYPE_NONE }}<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >}</span> };
+
+
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > 
 static maple_device_t vmu_new( )
 {
     vmu_device_t dev = malloc( sizeof(struct vmu_device) );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    dev->config.data = dev;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     memcpy( dev, &base_vmu, sizeof(base_vmu) );
     return MAPLE_DEVICE(dev);
 }
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -103,41 +110,38 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     vmu_device_t src = (vmu_device_t)srcdevice;
     vmu_device_t dev = (vmu_device_t)vmu_new();
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    lxdream_copy_config_list( dev->config, src->config );
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    lxdream_copy_config_group( &dev->config, &src->config );
+    dev->config.data = dev;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     return MAPLE_DEVICE(dev);
 }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static lxdream_config_<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >entry</span>_t vmu_get_config( maple_device_t mdev )
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static lxdream_config_<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >group</span>_t vmu_get_config( maple_device_t mdev )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
     vmu_device_t dev = (vmu_device_t)mdev;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    return dev->config;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return <span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >&</span>dev->config;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-static void vmu_set_config_value( maple_device_t dev, unsigned int key, const gchar *value )
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+static gboolean vmu_set_config_value( void *data, lxdream_config_group_t group, unsigned int key,
+                                      const gchar *oldvalue, const gchar *value )
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > {
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    vmu_device_t vmu = (vmu_device_t)d<span id="removedchars" style="background-color:#ff9999;font-weight:bolder;" >ev</span>;
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    vmu_device_t vmu = (vmu_device_t)d<span id="addedchars" style="background-color:#99ff99;font-weight:bolder;" >ata</span>;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     assert( key < VMU_CONFIG_ENTRIES );
     
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    if( value == vmu->config[key].value ||
-        value != NULL && vmu->config[key].value != NULL && strcmp(vmu->config[key].value, value) == 0 ) {
-        return; /* Unchanged */
-    }
-
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     if( vmu->vol != NULL ) {
         vmulist_detach_vmu(vmu->vol);
     }
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    lxdream_set_config_value( &vmu->config[key], value );
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >     vmu->vol = vmulist_get_vmu_by_filename( value );
     if( vmu->vol != NULL ) {
         vmulist_attach_vmu(vmu->vol, "MAPLE");
     }
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return TRUE;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 
 void vmu_attach(struct maple_device *dev)
 {
     vmu_device_t vmu = (vmu_device_t)dev;
</pre><pre id="removed" class="diff" style="margin:0; background-color:#ffdddd;" >-    if( vmu->config[0].value != NULL ) {
-        vmu->vol = vmulist_get_vmu_by_filename(vmu->config[0].value);
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    if( vmu->config.params[0].value != NULL ) {
+        vmu->vol = vmulist_get_vmu_by_filename(vmu->config.params[0].value);
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" >         if( vmu->vol != NULL ) {
             vmulist_attach_vmu(vmu->vol, "MAPLE");
         }
</pre><pre class="diff" style="margin:0;" ><small id="info" style="color: #888888;" >@@ -162,6 +166,7 @@
</small></pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > static int vmu_get_condition(struct maple_device *dev, int function, 
                       unsigned char *outbuf, unsigned int *buflen)
 {
</pre><pre id="added" class="diff" style="margin:0; background-color:#ddffdd;" >+    return 0;
</pre><pre id="context" class="diff" style="margin:0; background-color:#eeeeee;" > }
 static int vmu_set_condition(struct maple_device *dev, int function, 
                       unsigned char *inbuf, unsigned int buflen)
</pre></div>
<center><small>Chaos Theory</small></center>
</div></body></html>