June 5, 2018

VS Code: binding the same keys for next/prev change everywhere

In current VS Code, there are different actions for navigating changes:

  • workbench.action.editor.nextChange for going to next change in an editor
  • workbench.action.compareEditor.nextChange for going to next change in a diff viewer
  • editor.action.dirtydiff.next for viewing some sort of local diff in the editor
Plus of course equivalents for "previous change".

Now, I don't particularly care about the last one with a preview window. However, I want to move in the editor and in the diff viewer with the same keys.

This is not easy to do, because of VS Code's binding resolution order, and the built-in keybinding editor will not allow you to set it up right.

Here's a snippet of keybindings.json that does what I want. Binds Alt+J to "next change" and Alt+K to "previous change", both in diff and in regular editor. The key here is to set up two distinct when conditions.


    {
        "key": "alt+j",
        "command": "workbench.action.editor.nextChange",
        "when": "editorTextFocus"
    },
    {
        "key": "alt+k",
        "command": "workbench.action.editor.previousChange",
        "when": "editorTextFocus"
    },
    {
        "key": "alt+k",
        "command": "workbench.action.compareEditor.previousChange",
        "when": "isInDiffEditor",
    },
    {
        "key": "alt+j",
        "command": "workbench.action.compareEditor.nextChange",
        "when": "isInDiffEditor",
    },

August 17, 2017

scripting the Open Build Service from scratch

(Well, not entirely.)

In my day job I do packaging for openSUSE. My responsibility is taking care of the Python package ecosystem. Right now we are in the middle of one transition (to a different way of packaging Python modules) and starting another one (converting the distro to Python3-by-default). For both of these things I need to modify lots of packages at once.

The bulk of what I do involves the openSUSE Build Service, an instance of OBS that runs our distributions. The Build Service is a thing that allows us to build dozens of variants of each package for each of the supported distributions and architectures. It consists of several independent parts:
  • backend, which schedules and runs the actual build jobs on our server farm
  • API server, which allows us, the users, to control the backend, makes sure that files go where the backend can find them, etc.
  • web interface, which is a clickable client for the API. You can view packages, modify source files, configure build targets and so on
  • osc, the command line tool which is another client for the API.
osc would be the natural starting point for scripting. Unfortunately, osc is also a horrible mess that grew organically alongside the Build Service. It works perfectly fine as a end-user tool, but it's unwieldy for shell-based scripting and difficult to use as a library because it doesn't have a consistent enough internal design.
The main reason for this is that UI code is interwoven with API calls and local non-OBS functionality. Also, osc tries to emulate a version control system and bases its OBS interaction on this model.

There is also osc2, a from-scratch rewrite with the intent to split the UI and logic into separate parts and impose some sort of order on the overall chaos. Unfortunately, it is a typical Generation 2 Project, deeply layered, overly complicated and overly generic. And also not nearly feature-complete, for the obvious reason that it was mostly abandoned before it got anywhere.

We are considering some serious refactoring of osc, and it seems possible to reuse the functionality while fixing the structure. We also want Something Usable Now(tm). Hence my work on a tiny library called "osclib". The idea is to make it a thin wrapper around the API and gradually modify osc the command line client to use osclib where appropriate.
Hard to say if this will ever go anywhere, but osclib is a nice exercise in understanding the OBS API. Also, when scripting things, you often don't need the rich functionality of handling every possible special case and command line switch.

osclib relies on osc for parsing the config file (and extracting login information from it), but does its own HTTP communication through Requests. At the moment, it has one class (to wrap the API server connection), about five functions in total, and can accomplish what I wanted to do in the first place: download a list of every spec file in the Tumbleweed distro, let me modify them offline, then create a branch project for each touched package and upload the modified spec file into it.

The hardest part was not actually writing the code, but reading osc sources and the very sparse OBS API documentation and figuring out what to do. For example, in order to upload a file, you need to create a "commit" through a separate API request.

So the fact that I can write a simple script that performs the mass update I mentioned is a big win :)

You can find osclib on my github. It is currently part of a forked osc repo, because osclib is written in Python 3 and the system install of osc is in Python 2 and at this stage it's too much effort to manage the dependencies properly. So instead it's the neighboring directory and you simply add it to your PYTHONPATH.

April 1, 2013

setting up USB printer in Android/Linux chroot

I wanted to turn my Android tablet (an Asus Transformer TF101) into a little ad-hoc print server. Unfortunately, android apps for such purpose are either expensive, sucky or nonexistent (usually all three). So, I thought, since i have an openSUSE chroot up and running on the device, i can just install CUPS server and print happily through that.

This was not without trouble, because after installing it for the first time, it came up nicely, but refused to see the USB printer.

Here's how to fix it:

Step 1: Get a CUPS server that uses libusb. There is a known incompatibility between kernel support for USB printers and CUPS's userspace support - and since SUSE has kernel support turned on, userspace support is turned off. Well, it just so happens that Android has no kernel support and needs userspace support.
You can either compile a fixed version of CUPS yourself (just add BuildRequires: libusb-1_0-devel to the spec file), or install from my repository.

Step 2: Give yourself rights to the USB device in question. Look into /dev/bus/usb and make sure that the CUPS user can access the device file (for the simplest solution, chmod 666 /dev/bus/usb/*/* - beware though, this gives every user on the system access to all USB devices, so, you know. Exercise caution.)

Step 3: Start up the CUPS service. service cups start

Step 4: Head over to http://localhost:631/ in tablet's browser, set up your printer, allow remote administration, printer sharing, whatever you choose.

Step 5: Print!

April 3, 2012

fixing timestamps on Google Blogger's threaded comments

as you all probably know, time on comments on (google's) Blogger is broken, because it only shows Pacific timezone. You can't change it from settings, etc. etc... Fear not, for I have developed a cure!

You need to place a piece of javascript into the template. Here's how you do it:

Step 1: click on Design, then Edit HTML.
Step 2: Now, blogger will nag you about what you're doing and that you can break it... yeah, like we don't know. Proceed!!
Step 3: check "Expand Widget Templates"

Step 4: This is the tricky part.
First, using your browser's search function, search for "render = function".
You will find something like this:
      var render = function() {
        if (window.goog && window.goog.comments) {
          var holder = document.getElementById('comment-holder');
          window.goog.comments.render(holder, provider);
        }
      };

You need to change this part, so that it looks like this:
      var render = function() {
        if (window.goog && window.goog.comments) {
          var holder = document.getElementById('comment-holder');
          window.goog.comments.render(holder, provider);

/* THIS IS THE NEW PIECE */
          load = function() {
             load.getScript("https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js");
          }

          // dynamically load any javascript file.
          load.getScript = function(filename) {
             var script = document.createElement('script')
             script.setAttribute("type","text/javascript")
             script.setAttribute("onreadystatechange", "DOMLoaded()")
             script.setAttribute("onload", "DOMLoaded()")
             script.setAttribute("src", filename)
             if (typeof script!="undefined") document.getElementsByTagName("head")[0].appendChild(script)
          }

          load();
        }
      };

(script shamelessly stolen from here)

Now, scroll a bit more down, until you see:
    })();

// ]]>
  
between the "})();" and "// ]]>", we place the code that actually does something - fixes the dates and times!
    })();

function DOMLoaded(){
     $('.datetime').each(function() { var date = new Date(this.children[0].innerHTML + " PDT"); this.children[0].innerHTML=date.toLocaleString(); } );
};
// ]]>
  

Save, and voila! The times you see are now in your timezone.

What actually happened here is that I needed to inject jQuery plugin into the page, so that I could easily select the relevant elements. And the "render = function" seemed a good place, because it's part of some other javascript weirdnesses that make the whole threaded comment nonsense possible. The "DOMLoaded" then does all the work - parses the date as if it were in Pacific Daylight, which it happens to be, and converts it to your local time. Given more time, will and effort, you could customize the format, or use the load.getScript to pull in something like this and do magic with the dates. Or something completely different - now you have the full might of jQuery at your disposal, after all.

November 1, 2011

how to add FindErr search to Google Chrome

As Internet becomes mainstream and stupid people start using it (perhaps "nontechnical" would be better in many contexts, but in this case I believe "stupid" is more accurate), services must cater to the needs of the stupid. That's what happened at Google, who, apparently around 2009, started to search for "what the user meant" instead of what the user actually told them to search.
Now, this might be helpful in many cases, but it just so happens that I'm smart enough to recognize when bad search results are my fault, and if I search for a term, I want the search engine to give me the results for that damn term.

The Plus operator used to do that - if you're looking for "FindErr" and not "finder", just type "+FindErr" and it will give you what you wish. Alas, not any more: because of Google+, plus operator is used to search for people on G+. Now, instead, you have to put quotes around the word. Baaaah.

So anyway, I'm not the only one who is unhappy about this, and some people have already taken action. That's what the FindErr.org search engine is about. You type a sane search string, and it makes it all quotey before passing on to Google. Now if only there was a way to make this search the default in Chrome (or Chromium), my favourite browser.

And there is. Follow these simple steps:
  1. type finderr.org into the address bar, and load the page
  2. right-click the URL
  3. choose "Edit Search engines"
  4. in the list, locate finderr.org and click "Set Default"
There! All done!

February 2, 2011

how to run the whole testsuite in a Python project/module

I can't believe Google doesn't have anything to say about this...

The situation is usual: you have a python module, let's call it bravo, and a set of unittest-based unit tests in bravo.test. Now, there's nothing like "runtest" or whatever to run the test suite. Of course, you could run each of the tests individually, but maybe there's twenty of them and you're lazy, or maybe they don't even contain the magical "if name=main then runtest" spell.

Twisted to the rescue! Just run this:
trial modulename
or
trial modulename.test

If you don't have Twisted (of which trial is a part), you can use the following snippet:


import glob, unittest, os, imp
suite = unittest.TestSuite()
testloader = unittest.TestLoader()

for test in glob.glob("bravo/tests/test_*.py"):
name = os.path.splitext(os.path.basename(test))[0]
module = imp.load_source(name, test)
tests = testloader.loadTestsFromModule(module)
suite.addTest(tests)

unittest.TextTestRunner().run(suite)

December 9, 2010

how to fill your disk with random data

When using full-disk encryption, it is useful to prefill the disk in question with (pseudo)random data. This makes it harder to tell how much of the encrypted volume's space is already written to - in other words, how much data you have on the volume.

There are many ways to do it - specialized tools, reading from /dev/urandom (reasonably fast), reading from /dev/random (true randomness, but unless you have a HWRNG, it will take 1000 years to fill a disk). Trouble is, generating pseudorandom data is slow. While your average HDD can write at speeds over 50MB/s, you can only generate randomness at, say, 8MB/s (with one core, that is)

The usual recommended method is this:
dd if=/dev/urandom of=/dev/sda
It will take a very long time, because generating the random numbers is slower than writing them to the disk. The problem is that the kernel is only using one CPU core to generate the /dev/urandom stream - the CPU core on which your process runs.

Now if only there was some kind of a trick to make kernel use all four of my CPU cores...
You could, of course, run four dds and make them write to different areas of your disk - but wait, wouldn't that force the disk to seek back and forth? Wouldn't that be a little stupid? Yeah, I thought so.

That's why i wrote this tiny program called urandread. It will open four (or how many you need) processes to read from /dev/urandom, and then combine their output into a stream that is four times faster.
Then you can do this:
./urandread | dd of=/dev/sda
and you're BLAZING!

urandread.c

October 17, 2010

how to correctly execute su from Android application

This way:
Runtime.getRuntime().exec(new String[]{"/system/bin/su", "-c", "setprop ctl.stop zygote"});
If you are getting failures like this:
W/su ( 1043): request rejected (0:0->0:0 /system/bin/setprop)
that means that
  • you didn't send "-c", or
  • you didn't give it a parameter, or
  • you gave it more than one parameter
You have to make sure that after "-c" you only send one parameter. Alternate syntax would be this:
Runtime.getRuntime().exec("/system/bin/su -c 'setprop ctl.stop zygote'");

Starting and stopping Android core services from command line

Imagine, for example, that you need to stop the Zygote service and start it again later, because you're toying with Android internals and Zygote is getting in the way.
Or maybe you want to test out a new bootanimation binary, and for some reason running /system/bin/bootanimation directly is not what you want.
This is what you do instead:
setprop ctl.stop zygote
setprop ctl.start bootanim

Simple, right? Then how come it's ungooglable?

You can do all this from Java too, just use System.setProperty(). See this article for more detailed info about properties.

October 2, 2010

you gotta be effin kidding me

While trying to solve this issue, i have found a document from RIM's Knowledge Base about verification errors in Java packages.
This is it.
Some choice tidbits:
7. Comment out any non-executable code. Verification errors might be related to the size of the main code file and the library files. If you comment out non-executable code, the file sizes change, which may correct the problem.
2. Remove any System.out.* calls. These generally do nothing on the BlackBerry smartphone, but they might cause verification errors.
3. Remove unused import statements.

I have nothing. My mind is blown. How can anyone ever develop anything for this device?

May 7, 2010

how to tell git that you just want effin THEIRS version of a file

Imagine a situation: You are using git (maybe you like it, or maybe you just have to deal with it because people on the other end do that), you are starting to get familiar with it, make some changes to some source code, then want to synchronize with upstream.
You know that your changes are nothing dramatic, a line here, a word there.
So you go ahead and type git pull, expecting the merge to go seamlessly.
BAM! a conflict! (as it happens, upstream decided to throw out the file and put something completely different in its place)
No biggie, you say, i don't care about my changes, just give me their version. svn revert path/to/file would solve that in a whim.
First of all, you need to locate the file. "git status" won't tell you. Instead, type git commit.
It will say that there are conflicts and list them.
For each conflicting file, git checkout --theirs path/to/file will take the remote version. Similarly, --ours throws away remote changes.
Then just git add the files as usual, or simply go ahead and git commit -a. Done, voila!

March 29, 2010

vim folding makes me happy

When I'm not working with NetBeans and Java, I'm working with vim and python. Vim is unquestionably the best text editor of all time, but this post is not meant as an evangelism. This is aimed at those of you who are already using it.

Maybe you know that vim can do folding. That means that you type "zc" and the piece of code under your cursor neatly folds itself into one line (very much like in NetBeans/Visual Studio/Eclipse etc.), then type "zo" to unfold this line to the whole code. Alternately, use "za" to toggle.
This usually works either manually (you create folds with "zc" and "zf" and whatnot) or via a specialized filetype plugin that will create folds from, for example, sections delimited by curly braces.

It can also work based on indentation. Which happens to work very well for sanely formatted code, and especially for python, where sane formatting is part of syntax. Simplest way to get to this is to simply set foldmethod=indent in your .vimrc.

But this has its own share of drawbacks, namely, the autofolds do not contain a leading line.

For example, in this code:
if something():
do_stuff(1)
do_stuff(2)
do_stuff(3)

the autofolder will collapse the three do_stuff()s into one line labeled "+--- 3 lines:do_stuff(1)". I would like to collapse all four lines into one saying "if something()"

To accomplish this, you have to use a different method. This is what I added to my .vimrc (update: escaped the '<' sign that disappeared when rendering html):
setlocal foldmethod=expr
setlocal foldexpr=(getline(v:lnum)=~'^$')?-1:((indent(v:lnum)<indent(v:lnum+1))?('>'.indent(v:lnum+1)):indent(v:lnum))
set foldtext=getline(v:foldstart)
set fillchars=fold:\ "(there's a space after that \)


You also probably want to have all folds open by default:
set foldlevelstart=999


And to make things ultimately convenient, remap <SPACE> key to open/close a fold in normal mode
nnoremap <space> za

and create a new fold from selection
vnoremap <space> zf


There! Happy faces all around.

(note that this will mess up your foldlevel math, because there will be many foldlevels without corresponding folds. so don't expect serious use of zm/zr. me, i don't care.)

February 1, 2010

few notes on Samsung's IO

J2ME implementation on new Samsung phones has a few peculiarities. Yes, they're doing everything right by the spec - but they do it just slightly differently than most of the others.

First of all, InputStream.read() method, the one that reads single character, seems to be rather slow. When you say something like:
while (true) {
int ch = istream.read();
if (ch == -1) throw new IOException ("end of stream");
if (ch == '\n') break;
}

then the phone will do what you want, ssssssssllllllllllloooooooooowwwwwwwwwwwwllllllyyyyyyy.
Instead, make use of InputStream.available(), for example like this:
int av = istream.available();
if (av > 0) {
byte[] buf = new byte[av];
istream.read(buf);
} else {
int ch = istream.read();
}
The else is there for a reason. Two reasons, actually. First, some phones won't tell you what is available. So you should try to read anyway. Second, if nothing is actually available at that moment, read() will block and hopefully provide opportunity for other threads to run. (That is, if your phone implementation isn't completely retarded. Which, unfortunately, some can be.)

Now you've seen how Samsung reads from streams in large chunks. As it turns out, the chunks aren't as large as they could be.
Let's say you want to read a chunk of data from a file. Consider this:
DataInputStream dis = filehandle.openDataInputStream();
int length = dis.readInt();
byte[] buf = new byte[length];
dis.read(buf);

Can you spot the problem? Of course, the read(byte[]) method doesn't guarantee that it fills the buffer. But interestingly enough, on a vast majority of phones, it will actually do that when you're reading from a file.
Not on a Samsung.
So remember, kids, always check the return value of read(...) calls. Or, if you're using DataInput, as shown here, just readFully(...).

December 14, 2009

stuttering music in dosbox

If you have a slower computer (although i'm extremely reluctant to call my P4@2.8GHz "slower", it's a fact that my work computer with a Core2 just feels that much snappier) and want to play DOS games in dosbox, you might experience this:

The overall game performance is good, your CPU is not even fully taxed, sounds generally work as expected - all nice and fun, except for the MIDI music. Which crackles and skips and stutters and is generally unpleasant enough that you want to turn it off.

Fear not, for i have found a cure!

Open your dosbox.conf (you don't have one? head over to google and find yourself a nice set of instructions on how to create it and where to place it), locate the [mixer] section and increase the prebuffer value. I set mine to 50 (default seems to be 10) and the music is smooth like a cleanly polished watermelon. Or something.

August 2, 2009

openwig news

Version 0.3.92 is underway.
Biggest spectacle of this is cartridge saving and loading. A few moments ago i have successfully stored and restored a game of Wherigo Player Tutorial.
Technical details about the solution will follow in a separate article.
And that's pretty much it. There's the usual bunch of random bugfixes, minor improvements and extended Wherigo functionality (did i tell you that you can now see a zone map?), but probably nothing to write home about.

Oh, and i switched back from NetBeans' default proguard 4.2 (obfuscator/optimizer) to older 3.9. The new one is too aggressive in optimizing and still has some bugs. I identified and reported one, but it's not yet fixed, so i can't really continue the search. And i don't need the stress of hunting bugs that aren't there.

tidbits

S40 Nokias do weird things when you put a StringItem on a form, give it a label but no text. It will prevent users from scrolling that form.
S60 Nokias do some weird stuff too, but only sometimes. Best not to do this.

SonyEricssons, on the other hand, retain image size on ImageItem when you setImage(null). You have to set a dummy 1x1 image first (or instead).

And last but not least, with certain bluetooth devices (GPSr's, in this case), Nokia phone would perform a bluetooth service search, but return error instead of the service.
When you're searching for a serial port service (UUID 0x1101) and get an error instead, just take "btspp://" + RemoteDevice.getBluetoothAddress() + ":1" as a connection url and you have a solid chance of connecting successfully.
(That means BT address + channel 1. On single-purpose devices, you'll most likely find the only service on channel 1. Makes sense, eh?)

June 12, 2009

thoughts on Nokia class loading mechanism

situation:
Imagine you have your basic midlet class (say gui.Midlet). This class references another class (gps.InternalProvider) which implements a specific interface (gps.LocationService). Note that there is no reference to the actual class gps.InternalProvider, except for instantiation, and that is pretty well hidden in a function under several ifs and switches. Everything else is done through the interface.
Class gps.InternalProvider references classes from an api (JSR-179) that might or might not be present on your target device (S40 Nokia). It uses them rather extensively, but of course, it can't use them until it's instantiated. (no static codeblocks, nothing like that)

issue:
When such midlet is started on such device, it instantly dies with NoClassDefFoundError on the JSR-179 classes. It doesn't even start, I'm pretty convinced that none of my code is executed.
Definitely not the part that would instantiate the offending classes.

solution:
Create a distraction. Add a class (let's call it gps.InternalProviderRedirector) that has only one static method:
public static LocationService instantiate() {
return new InternalProvider();
}
Then instead of doing this directly in the Midlet class, call InternalProviderRedirector.instantiate(). Magically, it will work.

thoughts:
This is easy to grasp intuitively (for me, at least), but in some situations, that is not enough. So i studied the JVM specification, especially the parts about class loading and linking, and tried to come up with a scientific explanation to this phenomenon. Here's my best effort - note that it is only an educated guess and might not relate to reality in any way.

The spec says that when you are loading a class, you get symbolic references to all classes in use. Then, in the linking step, you can (but don't have to) resolve those symbolic references by trying to load the referenced classes.
I say that Nokia does this. That means that when linking the Midlet class, the class file for InternalProvider (or InternalProviderRedirector) is already loaded.
Then, either in the initialization phase or when the first code from a class is run, Nokia JVM attempts to link all the referenced classes. That means that those classes now try to load and resolve their symbolic references.
When you start the midlet, the class Midlet is loaded. Then it's linked, triggering loading of InternalProvider. And then it's instantiated, triggering linking of InternalProvider. That triggers loading of JSR-179 classes, which are not present, so the instantiation itself fails.
When you insert InternalProviderRedirector into the chain, InternalProvider is never linked (only loaded), so JSR-179 is never loaded. And all is well.

May 19, 2009

openwig news

Well well well. It sure has been a long time.
Today i have released a new and improved experimental version 0.3.90 (yup, that means pre-0.4), codenamed "Threadless". Why? Well, it's not because it makes t-shirts. It's because it doesn't use threads. Not too much of them, anyway.

Before this release, OpenWIG relied on threads for event synchronization. Whenever I needed to call an event handler, I spawned a thread that would perform the Lua code and die. Which is all good and nice, but can bring a lot of trouble. For one, when you use many threads, you need to care about (dead)locking. For two, some j2me implementations are somewhat inferior to the rest *cough*Symbian*cough*. Their garbage collection is simply not good enough. So it's better not to create too many objects, if you know what i mean.
Also, for the same reason, there is now a pre-allocated instance of each of the screen types (like "item details", "zone navigation", "dialog" etc.) and no new screens are created.

Oh, and screen switching is now stateless. That means that each screen knows in advance which screens can follow it. And that's cool! Lots of problems with screen refreshing disappeared this way.
And lot of them appeared. Oh well, for every bug you fix, three new are born.

In other news, we now have the latest and greatest Kahlua revision, which means that we support more obscure code. Inventories and AllZObjects are now accessible from Lua as tables. And some minor thingies, i don't remember exactly.

Anyway, the new version is very cool and you should all download it.

December 11, 2008

how to send JAR files to a Nokia from linux

Just so i don't forget next time.
  1. install gammu
  2. create a .gammurc:
    [gammu]
    # your phone BT address:
    port=00:02:5B:00:A5:A5
    connection=bluephonet
  3. cd /directory/with/jar/and/jad
    gammu --nokiaaddfile Application NameOfJarWithoutExtension

December 5, 2008

internal affairs filed under JSR179

OpenWIG 0.3.07 is out and it supports JSR-179 style "Location Providers" a.k.a. internal GPS.

Getting that to work was no picnic, though.

Here's a basic overview of how it's supposed to work:
  1. choose a list of things you want from your GPS device and set it all up in a Criteria object
  2. instantiate a LocationProvider by calling LocationProvider.getInstance(myCriteriaObject);
  3. periodically ask for a new location by calling provider.getLocation(timeout)
  4. or implement a LocationListener interface, and register that with the provider. You can then specify some refresh intervals and timeouts and whatnot. Fortunately, you can also leave it on default values.
Sounds rather easy, no?
Well, yes, but with a caveat: there's no listProviders or equivalent method. If the phone has more than one provider, it can return pretty much anything it wants.
And it turns out that e.g. Nokia N95 has more than one provider, and one of them is very dumb and coincidentally that's the one that is selected when you specify "empty" criteria (the default Criteria instance means basically "anything will do")

It seems to work when you say that you want to get speed, course, altitude and you allow it to cost money:
Criteria c = new Criteria();
c.setAltitudeRequired(true);
c.setSpeedAndCourseRequired(true);
c.setCostAllowed(true);
provider = LocationProvider.getInstance(c);
Oh and then there's the fun with invalid locations that can either have isValid flag set to false or be null. But that is easy to get right. Finding out that a phone is giving you a bad provider is the real pain.