Wolfmans Howlings

A programmers Blog about Programming solutions and a few other issues

Using Ruby SVN bindings to get file status

Posted by Jim Morris on Mon Sep 04 11:51:35 -0700 2006

If you Google around for information or even some documentation on the ruby SVN bindings you will find plenty of comments that it simply is not documented, so when I wanted to add an SVN status check to a UI I was working on (a project browser window for Rails), I had to "Use the source Luke". However given the bindings are actually mostly automatically generated by SWIG, and the actual details are hidden in a goo of swig generated c code, even that was a challenge.

Eventually I realized that the API was almost identical to the c level subversion API, not surprisingly, and for the most part it works the way you would expect. However I could not find any examples of the client status call, so here it is for anyone else struggling with this issue.

require "svn/core"
require "svn/client"
require "svn/wc"
require "svn/repos"

# define Consts for all the numeric status values
NONE = 1
UNVERSIONED = 2
NORMAL = 3
ADDED = 4
MISSING = 5
DELETED = 6
REPLACED = 7
MODIFIED = 8
MERGED = 9
CONFLICTED = 10
IGNORED = 11
OBSTRUCTED = 12
EXTERNAL = 13
INCOMPLETE = 14
# I added these so I can amalgamate repo and local status
UPDATED = 15
MODIFIED_NEWER = 16
NEWFILE = 17

$modes= { # Map status to English
    NONE => "None",
    UNVERSIONED => "unversioned",
    NORMAL => "normal",
    ADDED => "file added",
    MISSING => "missing",
    DELETED => "file removed",
    REPLACED => "deleted and then re-added",
    MODIFIED => "modified",
    MERGED => "received repos mods",
    CONFLICTED => "file modified and in conflict",
    IGNORED => "ignored",
    OBSTRUCTED =>  "unversioned resource is in the way of the versioned resource",
    EXTERNAL => "unversioned path populated by an svn:external property",
    INCOMPLETE => "directory doesn't contain a complete entries list",
    UPDATED => "newer version in repository",
    MODIFIED_NEWER => "modified and newer version in repository",
    NEWFILE => "new file in repository",
    nil => "Unknown status"
}

# this amalgamates the most common status between local and repository
def compute_status(status)
  # if modified in repo and localy
  return MODIFIED_NEWER if status.repos_text_status == MODIFIED && status.text_status != NORMAL
  # if added in repo and does not exist locally
  return NEWFILE if status.repos_text_status == ADDED && status.text_status == NONE
  # if repo is none then use local status
  return status.text_status if status.text_status != NORMAL
  # if normal locally but changed in repo
  return UPDATED if status.repos_text_status == MODIFIED
  # otherwise it must be normal
  NORMAL
end

ctx = Svn::Client::Context.new()

rev = ctx.status("/home/user/project/myproject", "HEAD", true, true) do |path, status|
  astat= compute_status(status)
  puts "#{path}: #{status.text_status},#{status.repos_text_status} = #{$modes[astat]}"

  unless status.entry.nil?
    puts(".....name: #{status.entry.name}")
    puts(".....url: #{status.entry.url}")
    puts(".....repos: #{status.entry.repos }")
    puts(".....revision: #{status.entry.revision}")
    puts(".....kind: #{status.entry.kind}")
    puts(".....schedule: #{status.entry.schedule}")
    puts(".....deleted: #{status.entry.deleted}")
    puts(".....absent: #{status.entry.absent}")
    puts(".....incomplete: #{status.entry.incomplete}")
    puts(".....cmt_date: #{status.entry.cmt_date}")
    puts(".....cmt_rev: #{status.entry.cmt_rev}")
    puts(".....cmt_author: #{status.entry.cmt_author}")
    puts(".....prop_time: #{status.entry.prop_time}")
    puts(".....text_time: #{status.entry.text_time}")
  end
end

This is a sample, it shows how to setup the client context, call the status call, and parse the results. I also added some sugar by defining the numeric status codes and adding a hash to translate them to english. I also compute an amalgamated status code from the local status and status in the repository, to get the extra status codes I added above.

The status parameter returned by the status() call turns out to be a standard svn status structure, containing various fields, the most interesting one as far as status is concerned are text_status and repos_text_status fields which are numeric fields which specifies the SVN status of the file locally and in the repository, I have created in the example above a bunch of constants to define each of the states, and a hash to turn them into english.

Note the compute_status() method which looks at both the local status and repository status to see what the relative state of any file is wrt the repository.

The other interesting field is the entry field, which contains bunch of data as shown above, and in the structure definition below.

There are more parameters that can be passed to the status call, but the last two I show should be set to true, as that allows recursion into sub directories and shows all files, by default recursion is true but all_files is false which only show files not under svn control.

The developers have made some attempt to make this relatively easy to use, you can forget about the apr pools and stuff, and the second parameter is usually a structure that tells it the version or revision to search for, this has been simplified so you can pass in a string describing the revision ("HEAD", "TAIL", etc) or a number denoting the revision number.

The status and entry structures have also had the types converted into convenient ruby types, but for reference I show the c struct definitions from the various svn header files...

Here is the full status structure from svn_wc.h

typedef struct svn_wc_status2_t
{
  /** Can be @c NULL if not under version control. */
  svn_wc_entry_t *entry;

  /** The status of the entries text. */
  enum svn_wc_status_kind text_status;

  /** The status of the entries properties. */
  enum svn_wc_status_kind prop_status;

  /** a directory can be 'locked' if a working copy update was interrupted. */
  svn_boolean_t locked;

  /** a file or directory can be 'copied' if it's scheduled for
   * addition-with-history (or part of a subtree that is scheduled as such.).
   */
  svn_boolean_t copied;

  /** a file or directory can be 'switched' if the switch command has been
   * used.
   */
  svn_boolean_t switched;

  /** The entry's text status in the repository. */
  enum svn_wc_status_kind repos_text_status;

  /** The entry's property status in the repository. */
  enum svn_wc_status_kind repos_prop_status;

  /** The entry's lock in the repository, if any. */
  svn_lock_t *repos_lock;

}

and the entry structure

typedef struct svn_wc_entry_t
{
  /* IMPORTANT: If you extend this structure, check svn_wc_entry_dup() to see
     if you need to extend that as well. */

  /* General Attributes */

  /** entry's name */
  const char *name;

  /** base revision */
  svn_revnum_t revision;

  /** url in repository */
  const char *url;

  /** canonical repository URL or NULL if not known */
  const char *repos;

  /** repository uuid */
  const char *uuid;

  /** node kind (file, dir, ...) */
  svn_node_kind_t kind;

  /* State information */

  /** scheduling (add, delete, replace ...) */
  svn_wc_schedule_t schedule;

  /** in a copied state */
  svn_boolean_t copied;
  /** deleted, but parent rev lags behind */
  svn_boolean_t deleted;

  /** absent -- we know an entry of this name exists, but that's all
      (usually this happens because of authz restrictions)  */
  svn_boolean_t absent;

  /** for THIS_DIR entry, implies whole entries file is incomplete */
  svn_boolean_t incomplete;

  /** copyfrom location */
  const char *copyfrom_url;

  /** copyfrom revision */
  svn_revnum_t copyfrom_rev;

  /** old version of conflicted file */
  const char *conflict_old;

  /** new version of conflicted file */
  const char *conflict_new;

  /** working version of conflicted file */
  const char *conflict_wrk;

  /** property reject file */
  const char *prejfile;

  /** last up-to-date time for text contents (0 means no information available)
   */
  apr_time_t text_time;

  /** last up-to-date time for properties (0 means no information available) */
  apr_time_t prop_time;

  /** base64-encoded checksum for the untranslated text base file,
   * can be @c NULL for backwards compatibility.
   */
  const char *checksum;

  /* "Entry props" */

  /** last revision this was changed */
  svn_revnum_t cmt_rev;

  /** last date this was changed */
  apr_time_t cmt_date;

  /** last commit author of this item */
  const char *cmt_author;

  /** lock token or NULL if path not locked in this WC
   * @since New in 1.2.
   */
  const char *lock_token;
  /** lock owner, or NULL if not locked in this WC
   * @since New in 1.2.
   */
  const char *lock_owner;
  /** lock comment or NULL if not locked in this WC or no comment
   * @since New in 1.2.
   */
  const char *lock_comment;
  /** Lock creation date or 0 if not locked in this WC
   * @since New in 1.2.
   */
  apr_time_t lock_creation_date;

  /* IMPORTANT: If you extend this structure, check svn_wc_entry_dup() to see
     if you need to extend that as well. */
} svn_wc_entry_t;

For the most part you can simply reference the fields in the structure and get a relatively understandable result.

The status call is defined in ruby as this

def status(path, rev=nil, recurse=true, get_all=false,
           update=true, no_ignore=false,
           ignore_externals=false, &status_func)

The first parameter is the path on the local file system to get the status of, the second is the revision to get, something like "HEAD", or 2345 can be passed in, the next parameter is whether to recurse into subdirectories, the next tells it to return the status of all files if set to true, default of false and only returns "interesting files" ie local mods and/or out-of-date or not versioned. Update is set to true it will contact the repository to get more information wrt to the version specified in the first parameter (which is ignored otherwise). I couldn't find any documentation in the c stuff about the no_ignore parameter, but the ignore_externals tells it to return status on externals or not. See the header file svn_client.h and the call svn_client_status2 for more documentation.

Posted in Ruby,svn  |  Tags ruby,subversion,svn,rubysvn  |  3 comments

Show

Getting f-spot to upload to flickr on KUbuntu

Posted by Jim Morris on Sun Aug 27 20:29:43 -0700 2006

f-spot is a good photo importer and organizer, but it is a gnome application. When I installed it on KUbuntu, which uses KDE, using aptitude install f-spot it worked ok, except I got the following error when I tried to send my photos to flickr.

GLib.GException: There is no default action associated with this location

After some Googling I found this solution...

> gconftool-2 --set --type=string /desktop/gnome/url-handlers/http/command '/opt/firefox/firefox %s'

my Firefox is in /opt/firefox, so you may have to change that path, or even point to a different browser.

Make sure gconftool-2 is installed (it was on mine after installing a bunch of gnome apps).

It sets the default browser, and after this I was able to upload to flickr.

Posted in Linux  |  Tags kubuntu,flickr,fspot  |  no comments

Show

Experiences with Komodo Pro and Ruby

Posted by Jim Morris on Wed Aug 09 22:39:06 -0700 2006

I got a license for Komodo Pro, (Komodo-Professional-3.5.3-262321-linux-libcpp5-x86) and started trying to use it, I have the latest KUbuntu, with GTK installed so I had no problems installing Komodo as far as libraries and requirements were concerned, however running it had me stumped, I ran into immediate problems (some of my own making I'll admit).

First I saw a stream of errors on the screen, they didn't seem fatal but are annoying, for instance...

(Gecko:24458): GdkPixbuf-CRITICAL **: gdk_pixbuf_get_width: assertion `pixbuf != NULL' failed
(Gecko:24458): GdkPixbuf-CRITICAL **: gdk_pixbuf_get_height: assertion `pixbuf != NULL' failed
(Gecko:24458): GdkPixbuf-CRITICAL **: gdk_pixbuf_get_rowstride: assertion `pixbuf != NULL' failed
(Gecko:24458): GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT (object)' failed
(Gecko:24458): Gdk-CRITICAL **: gdk_pixbuf_get_from_drawable: assertion `gdk_window_is_viewable (src)' failed

For a commercial product costing around $299 that seemed a little unnecessary.

The first problem was my making I launched Komodo from the command line in the installation directory, then deleted the installation directory, which caused it to have hysterics about chdir etc, although to be honest an xwindows IDE probably shouldn't care where it is launched from, as it would normally be launched from a button or desktop icon.

Tech support was very fast in responding to my email though, and they didn't even know I had a pro license. They asked a bunch of questions, which I answered, but seeing as the remaining problems were non-fatal I wasn't too concerned about the response, which I haven't got yet, but I would like to know if all those GTK errors are normal.

The more disturbing problem was when I tried to write a simple Ruby script, I expected completion as you would see with Java and Eclipse, but instead typing:

str= ""
str.

gave me an error:

 error determining members: don't know how to make a suitable dumb_get_members for Ruby

This error was what I got at first, after going back and typing str. again I got the expected list of possible completions, seems this happens the first time every time, either it is slow figuring out what to complete or it is very buggy. (Update see later, it is the former, it is slow figuring out what type something is).

Time.

Did give me a list of completions most times. When it does decide to offer a list of completions, it also shows the parameters for the calls, like Eclipse/Java does, that's nice, I hope it is reliable though.

OK got word back from tech support, apparently it can take "a couple of seconds" for Komodo to catch up with typing, so my example of...

str= ""
str.

Would probably not work unless I waited a few seconds between the two lines, I think this may be acceptable as it is unlikely to be an issue in normal use. In practice it seems to be more than a couple of seconds BTW.

The main reason for me trying Komodo was for the code completion, without intelligent code completions I really don't see the use for an IDE, I can use my trusty Epsilon editor from Lugaru, with the ruby syntax highlighting and formatting extensions and my own contribution to the Epsilon library snippets, much like the great Textmate snippets (although not nearly as complete), there are also various user contributed extensions for various source code code control systems etc here.

Yes Epsilon is quite expensive for an editor, but I have been using it for nearly 20 years, upgrading every few years or so, it is basically Emacs with a c-like extension language (instead of lisp), it is very solid, and runs on all the platforms I use (Mac OS/X, Windows and of course Linux). It does not have code completion of course (although I think I can write an extension ala Vim7 to do that, watch this space).

Maybe I should mention what I am looking for in an IDE for Ruby and Rails...

I want:

  • syntax highlighting
  • auto indentation
  • auto-correction (or at least error highlighting, or suggestions)
  • snippets (or templates)
  • code completion (showing the possible methods for a variable called intellisense or intelligent code completion by some)
  • word completion based on something in the source already, or from a tags list or from a known-word list
  • showing the parameters for a given method I am currently typing
  • full debugging (with breakpoints, watches, stepping and variable examination)
  • SCCM integration (subversion, CVS, perforce)

Basically everything I get when I use Eclipse and Java.

I get some of this with Epsilon, and Textmate but they are not IDEs so do not have debugging or code completion, or error detection.

Back to Komodo...

It has Perforce, CVS and Subversion bindings built-in, and does show the files status in the project view.

The auto-indent works nicely, unlike RadRails which I tried but just didn't get along with as I kind of expect it to be as good as Java and Eclipse, but my main complaint is it doesn't even auto-indent. (I think intentionally).

The syntax highlighting in Komodo is OK but not great, I think that can be tweaked, but I haven't looked that closely.

The debugging does seem to work well, you can set breakpoints, and step over, into and watch variables, everything you would expect. This may be worth the price of admission (YMMV).

It does show you highlighting when it detects errors as you type, within the limitation of Ruby syntax.

The Snippets implementation is pathetic compared to Textmate or Eclipse, you can't specify tab stops, you can't type the trigger word for a snippet and have it expanded on TAB, you have to either bind it to a key sequence, or double click the snippet name in the toolbox, that is simply clunky, and pretty much unusable. There are *NO* built-in snippets, you have to create them all yourself, so if you like typing "for" then tab and have a for loop created forget it, unless you create it yourself then you have to find the "for" snippet in the toolbox and double click it. I also couldn't find any on the web, I think for the price of this product they should supply most of the expected snippets. The other thing I don't like is the snippets will pop up a dialog box asking for input, instead of the usual way of just highlighting the text and typing over it to replace it.

My initial impressions is the editor part is clunky, given the excellent examples of Eclipse and Textmate, it simply doesn't compare as far as modern editing conveniences such as snippets are concerned.

The debugging seems capable, and probably the best I have seen for Ruby.

The code completion is slow and doesn't always work.

The source code control seems a little buggy in some places, although only from a UI standpoint. (Pops up strange dialog boxes on updates saying files need to be added, when they don't).

A serious bug is if the file changes on disk, (like if you do a revert), you get a dialog popping up saying the file has changed on disk, but you can't get it to go away, clicking OK just pops it up again, ad infinitum, you need to kill the program to move on.

All in all I think this would be a nice IDE if it were free, and the personal version may well be useful for some if you want decent debugging, but I simply can't see a justification for $299 for the pro-version. (At least not for this version). There is an alpha for the next version that claims to support Rails development, if they also fix the snippets and the code completion (which they have not in the current alpha), then we may be getting close to value for the money, but right now I think they have a long long way to go before this product justifies the price tag, and comes close to competing with the few other editors out there let alone IDEs.

Update

Tech support have been very responsive, and they came up with a macro that inserts snippets almost the way I expect,

komodo.view.setFocus();
try {
    komodo.editor.vCHomeExtend();
    var snippet = komodo.interpolate('%s');
    var tmpl = komodo.findPart("snippet", snippet, "container");
    if(tmpl) {
        Snippet_insert(tmpl);
    } else {
        var msg = "no snippet found named " + snippet;
        StatusBar_AddMessage(msg,"debugger",5000,true);
    }
} catch(e) {
    alert(e);
}

Add this as a macro, bind it to a key (say ALT-=), and then add your snippets to the same folder as the macro is in, name them appropriately, and the type im the name hit ALT-= and the snippiet is found and inserted, pretty neat.

Disclaimer: These are my personal opinions, I am not affiliated with any editor or IDE manufacturers.

Posted in Rails,Ruby  |  Tags komodo,ide,ruby,rails,editors,epsilon  |  1 comments

Show

Switching to KUbuntu from Redhat 9

Posted by Jim Morris on Mon Jul 03 13:07:40 -0700 2006

OK it was time I upgraded my main development workstation from a highly modified Redhat 9 to something more up to date that actually gets security updates and has a good package manager.

I have been using Ubuntu for my servers for some time, and like the package management, but I am used to KDE now and don't really want to switch to Gnome on my workstation. So I decided to try to upgrade to KUbuntu.

Upgrading the actual Redhat 9 seemed a bad move, so I bought a new 400GB drive (the Seagate ST3400633A, as its quiet and Fry's had it in sale for $118). The plan was to do a fresh install of KUbuntu onto that then copy over the stuff I need. After about 2 years of installing stuff and upgrading the kernel manually on the Redhat 9, this was going to be quite a task.

First up Dual monitors.

Unlike Windows and Mac OSX this doesn't "just work". On my RH9 system, I had the proprietary ATI driver fglrx, running an old XFree86 Version 4.3.0 server, and was only able to get two KDE desktops side by side but completely separate, this was OK, but part of the upgrade I wanted to be able to drag windows across monitors just like on Windows and OSX. My Video card was an ATI with a DVI and VGA connector which can both be ised at the same time.

After Googling it turned out some people found this easy and some hard, I finally got it to work, by installing the fglrx using aptitude install, getting the aticonfig to set up dual monitors, then telling KDE I had dual monitors. Seems you need to do both, oh and you need to actually power cycle between the changes to xorg.conf. Initially if I did not do the KDE setup step I got two screens but the cursor and windows were stuck on the first screen and KDE would not let the cursor or windows into the second screen. What seems to have happened is the latter step added the option "Xinerama", even though everything I read said the fglrx drivers didn't need that option. (BTW it seemed to work on the standard Gnome version of Ubuntu without telling Gnome anything).

So following:

> sudo aticonfig --initial --dtop=horizontal

I ran the System/Setup selected the monitor panel and set it to dual screen. then rebooted.

Ending up with this xorg.conf...

Section "ServerLayout"
  Identifier "Default Layout"
  screen 0 "Primary Screen" 0 0
  screen 1 "screen1" rightof "Primary Screen"
  InputDevice "Mouse0" "CorePointer"
  InputDevice "Keyboard0" "CoreKeyboard"
  InputDevice "DevInputMice" "AlwaysCore"
EndSection

Section "Files"

  # path to defoma fonts
  FontPath "/usr/share/X11/fonts/misc"
  FontPath "/usr/share/X11/fonts/cyrillic"
  FontPath "/usr/share/X11/fonts/100dpi/:unscaled"
  FontPath "/usr/share/X11/fonts/75dpi/:unscaled"
  FontPath "/usr/share/X11/fonts/Type1"
  FontPath "/usr/share/X11/fonts/100dpi"
  FontPath "/usr/share/X11/fonts/75dpi"
  FontPath "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType"
EndSection

Section "Module"
  Load "i2c"
  Load "bitmap"
  Load "ddc"
  Load "extmod"
  Load "freetype"
  Load "int10"
  Load "type1"
  Load "vbe"
  load "glx"
  load "dbe"
  load "v4l"
EndSection

Section "InputDevice"
  Identifier "Keyboard0"
  Driver "kbd"
  option "XkbModel" "pc105"
  option "XkbLayout" "us"
  option "XkbRules" "xorg"
EndSection

Section "InputDevice"
  Identifier "Mouse0"
  Driver "mouse"
  option "Device" "/dev/input/mice"
  option "Protocol" "IMPS/2"
  option "Emulate3Buttons" "no"
  option "ZAxisMapping" "4 5"
EndSection

Section "InputDevice"
  Identifier "DevInputMice"
  Driver "mouse"
  option "Protocol" "IMPS/2"
  option "Device" "/dev/input/mice"
  option "ZAxisMapping" "4 5"
  option "Emulate3Buttons" "no"
EndSection

Section "Monitor"
  identifier "primarymonitor"
  vendorname "NEC"
  modelname "NEC MultiSync LCD1860NX (Analog)"
  HorizSync 31.0-82.0
  VertRefresh 55.0-85.0
  modeline  "1280x1024@60" 108.0 1280 1328 1440 1688 1024 1025 1028 1066 +hsync +vsync
  gamma 1.0
EndSection

Section "Device"
  identifier "Ati Radeon 9800 Pro 0"
  boardname "ati"
  busid "PCI:1:0:0"
  driver "fglrx"
  screen 0
EndSection

Section "Screen"
  Identifier "Primary Screen"
  Device "Ati Radeon 9800 Pro 0"
  Monitor "primarymonitor"
  DefaultDepth 24
  SubSection "Display"
    depth 24
    modes "1280x1024@60"
  EndSubSection
EndSection

Section "DRI"
  Mode 0666
EndSection

Section "device" #
  identifier "device1"
  boardname "ati"
  busid "PCI:1:0:0"
  driver "fglrx"
  screen 1
EndSection

Section "screen" #
  identifier "screen1"
  device "device1"
  defaultdepth 24
  monitor "monitor1"
  SubSection "Display"
    depth 24
    modes "1280x1024@60"
  EndSubSection
EndSection

Section "monitor" #
  identifier "monitor1"
  vendorname "Plug 'n' Play"
  modelname "Plug 'n' Play"
  modeline  "1280x1024@60" 108.0 1280 1328 1440 1688 1024 1025 1028 1066 +hsync +vsync
  gamma 1.0
EndSection

Section "ServerFlags"
  option "Xinerama" "true"
EndSection

I had to hand edit the file to delete the duplicate monitor and screen definitions that were in there.

Installing Ruby, Gems and fxruby

I installed Ruby using the package manager, its just a matter of making sure you select everything you need. The biggest problem I had was getting fxruby working (1.4.6 version). I installed fox1.4 from the package manager then loaded the fxruby gem using gem install. However none of my ruby progs using fox14 would work, they reported they couldn't load fox14. After much installing and uninstalling, the result was I found a bug in the fxruby gem install, the Makefile calls make clean after building the fox14.so, and happily deletes it after building it.. Duh!! I need to report that to the fxruby maintainer, it burnt about 2 hours of my time. What I did to fix it was simply...

> cd /usr/lib/ruby/gems/1.8/gems/fxruby-1.4.6/ext/fox14
> make

Update, according to the maintainer this is a bug in the latest release of gems 0.9.0

(BTW I wish Debian/Ubuntu put ruby in /usr/local where it belongs!)

Thunderbird setup

I simply copied the .thunderbird directory over to my new home, and everything seemed to work ok, except clicking on links in my email no longer worked. I had to edit my ~/.thunderbird/XXXXXXXXXXXX.default/prefs.js file and add the following...

user_pref("network.protocol-handler.app.ftp", "/opt/firefox/firefox");
user_pref("network.protocol-handler.app.http", "/opt/firefox/firefox");

Where XXXXXXXXX will be different on your machine, and also the path to firefox.

Also I copied my ~/.mozilla folder and firefox got al my bookmarks etc from before.

Firefox setup

Clicking on mailto links didn't work, this was fixed by adding this line

user_pref("network.protocol-handler.app.mailto","/opt/bin//thunderbird");

to the user.js file (create a new one if it doesn't exist) that is found in ~/.mozilla/firefox/default.xxx

Of course the .xxx will be different on your system as will the path of thunderbird.

Keyboard Mapping

I like to map my caps lock key to the Win key, so I can use it to switch windows and desktops etc, I use a Unicomp Linux keboard, as it has a nice feel, with loud clicky keys, and the control key is where it is supposed to be, next to the A key, so it doesn't have a Win key, but the caps lock key is generally useless, so I use that.

It turns out that the Dapper version of KDE does not use the ~/.Xmodmap file as it is supposed to, so you need to manually run

> xmodmap ~/.Xmodmap

One way that has been suggested to automate this is to put that command in the ~/.kde/Autostart, so that is what I did, I called the file mapwinkey, set chmod +x, put xmodmap $HOME/.Xmodmap in it, and put the shell script in the Autostart directory.

More to come as I do it...

Migrating KDE settings

CPU/HW monitoring

Migrating applications

Posted in Linux  |  Tags linux,ubuntu,firefox,thunderbird,dualmonitor,xmodmap  |  no comments

Show