Oct 16

Endianess in Computers

The other month, I was working on some code where I had to be concerned about the endianess of the data.  I came across this article from IBM which greatly cleared up some things(I got to the article at least partially from this Stackoverflow quesetion).

Now I’m going to deviate here as that was almost a month ago and I forget what I was going to talk about here.

Anyway, the point here is that when you do any sort of bit-shift or bit-masking, it is always in the code as big-endian format.  When you do an operation, it is always big-endian but how it gets stored differs on what machine you are on.  To get the actual layout of the byte as they are in-memory, I generally cast the data as a uint8_t*.  So for example, let’s take the following code:

uint32_t value = 0x12345678;
uint8_t* value_memory = (uint8_t*)&value;

for( int x = 0; x < 4; x++ ){
    printf( "0x%02X ", value_memory[ x ] );
}

With this code, there will be two different outputs depending on what endianess your machine is in.  On a big-endian machine(like MIPS) this will print out something like the following:

0x12 0x34 0x56 0x78

However, on a little-endian machine(x86, ARM) it will print out the values in the opposite direction:

0x78 0x56 0x34 0x12

The thing to remember here is that the ‘value’ variable, when printed out on both big and little-endian systems, will print out as the same value.

If you need to force a particular byte order, make an array in the code(this is shown in the IBM article).  If you need to convert a particular byte sequence into the proper endianess for your processor, you can simply OR the two bytes together.

For example, let’s assume that we get the following two bytes in big-endian order:

uint8_t bytes[] = { 0x12, 0x34 };

To convert this to your processor endianess as a 16-bit value, you only have to OR the two bytes together, and since OR-ing will always act as though the bytes were in a big-endian format, your data has magically become the proper endianess.

uint16_t proper_endianess = (bytes[ 0 ] << 8 ) | bytes[ 1 ];

Anyway I hope that somewhat clears some things up.  (Although I think I may have simply confused people more).

Aug 20

Libraries and Dependencies

Have you ever used a library in a project, only to find that it depends on other libraries, only to depend on other libraries?

I’m willing to bet that you have.  Fortunately, there exist tools in the world to help manage these dependencies.  In the Java world, Maven is the de facto standard in this regard.  It’s quite convenient to manage your dependencies.  On Windows, NuGet is the de facto standard.  (Note: I have never used NuGet.  The number of dependencies can quickly get staggeringly huge though, so that is at least part of the reason that these tools exist.

Why do I bring this up? Well, at work we use jOOQ for our database operations.  It’s an awesome library that you should use if you need to do database operations in Java.  They also have a blog that I read from time to time, as there is some good information included in it.  One of their more recent blog posts talks about libraries and having zero dependencies.  Basically, the argument here is that all libraries should have zero dependencies, because of getting into a dependency hell(although they do carve out an exception for frameworks such as Spring and Java EE).

I don’t fully agree with this statement, but it is a good time to talk about these dependencies.  As far as I am able, none of the libraries that I have made have any dependencies(that being said, I only have one library on Maven: JavaSerial).

Why don’t I fully agree with the statement?  Well, in many cases I don’t think that it is entirely unreasonable to have a dependency, although this can get into a very gray area.  In the case of jOOQ, it would not be entirely unreasonable to have a dependency on commons-lang, because there are two different artifacts for commons-lang(one for version 2.x[commons-lang] and one for version 3.x[commons-lang3]).  On the other hand though, if there is only one class that you need from a certain dependency, it is reasonable to simply wrap that class as jOOQ does in order to provide the same level of support.

With that being said, here is a set of questions you should ask yourself when deciding if you need a hard dependency of a library:

  • What is the dependency attempting to provide?  For example, SLF4J is a reasonable dependency to have, since many libraries already use it for logging(although, if at all possible, you should use java.util.logging instead)
  • How much of the dependency do you need?  If you only need a single class from commons-lang as jOOQ does, pulling in the full dependency does not make sense.  If you use a significant portion, then it may make sense.  This is probably the most important question you have to ask.  If you are doing a lot of reflection, then it may be worth it to get a library to help you do reflection.  If you only need to do it once, it probably doesn’t make sense.
  • How widespread is the library?  Commons-lang is a widely used library, so having it as a dependency is not likely to cause problems.
  • Are there different versions of the library that people may be using?  As shown in the jOOQ blog post, if you are using Guava 15 while another library uses Guava 18, will that cause problems?  I have never used Guava, so I don’t know if it would actually cause a problem, depending on how the API has changed.

I realize that this is not a comprehensive list, nor is it a perfect guide.  Ultimately, as a library writer you should try as much as possible to keep the number of dependencies down as low as possible.  Otherwise, it can quickly lead to dependency hell for the end users, and potentially for you as well.

Dec 30

SQLite and debugging

I have just spent the last few days and weeks trying to track down a very annoying, yet somewhat subtle bug in a program that I have been making for work.  So, first a little background.  We are making a gateway that talks over radio to other, remote lamps in order to control them and get status back.  One thing that it must be able to do is to change the frequency of a remote lamp.  In order to do this, you must first change your local frequency(if it has to be changed) and then tell the lamp the new frequency to go to, and ensure that you can still talk to the lamp once that has happened.  This is all controlled by a web page, so the sequence looks something like this:

Web Request --> (http) --> Gateway --> (serial, 115200) --> Radio --> (RF) --> Lamp Radio

However, we were having a problem with this sequence.  Almost all the time, the responses that we were getting back from the radio were short by a few bytes.  Sometimes it would be 1, other times it would be 15 bytes that we missed.  Moreover, the bytes that were dropped changed position.  Generally it wouldn’t really matter, since there were a bunch of unused bytes that were all initialized to 0x00, but we couldn’t tell where in that sequence the bytes were getting dropped.  Now let’s go into some pseudo-code here.  This is what the update looked like:

void update( Map<String,Object> JSONData ){
    if( JSONData[ "attr1" ] )
        writeToDatabase( JSONData[ "attr1" ] )
    // ..repeat the above a few times

    if( JSONData[ "frequency" ] ){
        //the frequency needs to be updated
        new ThreadToUpdateTheFrequency().start();
    }

    if( JSONData[ "attr4" ] )
        writeToDatabase( JSONData[ "attr4" ] )
    //repeat the above a few more times

There’s not anything wrong with this code.  Sure, it’s probably inefficient, but that’s somewhat besides the point.  This code works sometimes, however a large portion of the time it will randomly drop bytes in the new thread that is started if the frequency is changed.

To debug this, I tried everything that I could think of.  Timing the sequence between us writing out data to the radio and receiving it back, writing data out at random times, etc.  Many hours were spent on this in order to figure out what was going on.  One of the things that we noticed though was that this only happened at certain times – when we were actually writing to the database.  If we were writing out commands without a write to the database, everything worked properly.  Moreover, if we stepped through the process in the debugger, everything would work properly.

What?  Why would the database cause a problem?

Well, let’s back up a bit and talk about the hardware that this is running on.  Linux is using an SD card as the root filesystem, and the processor is an Atmel SAMA5D3.  This is not a particularly fast processor, in addition to being only single-core.  What finally fixed the problem is the following:

void update( Map<String,Object> JSONData ){
    if( JSONData[ "attr1" ] )
        writeToDatabase( JSONData[ "attr1" ] )
    // ..repeat the above a few times for ALL ATTRIBUTES

    if( JSONData[ "frequency" ] ){
        //the frequency needs to be updated
        new ThreadToUpdateTheFrequency().start();
    }

Why would moving the new thread to the end of the updates fix anything?  Well, here’s the basic theory that I have come up with:

SQLite is writing out to the database.  When it does that, it attempts to lock the database(see the documentation for some more details).  This takes quite a bit of time, especially talking to the SD card which is running at just a few megabytes per second at the most.  At some point during this time, a new interrupt comes in.  However, the kernel does not process the interrupt fast enough before a new character comes in on the serial line, and thus some data will get dropped since the processor is assuming that the data has been fully read.

Oddly enough, dmesg does not show many problems; I only get a few input overruns.  It looks like there could be a kernel bug here compounded by how fast the SD card can be read from.

Aug 29

Simple JAX-RS example & Maven configuration

So I have been using JAX-RS for the past few months now, and it was a little confusing to get started with.  To fix that, I’ve created a simple project on github that contains a minimal configuration for Maven in order to get a war file and have Jersey be the JAX-RS provider.

The other reason to do this is because the Jersey documentation is not very clear as to what you have to pull in from Maven Central in order to actually use the dependencies in a program.  Turns out, it’s just a single dependency, but finding it and making sure that it works is another matter.

Lastly, I just came across this page, which also has some good JAX-RS examples.  I hope that they help somebody!

May 27

udev rules for multiple interfaces

I just had to write a udev rule for Linux to interpret between several different endpoints on a single cellular modem, specifically this modem.  This shows up in Linux as 5 ttyUSB ports.  In order to make sure that the ports do not change around on me, I checked the following with udevadm:

$ udevadm info -a -n /dev/ttyUSB1

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

 looking at device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1/tty/ttyUSB1':
 KERNEL=="ttyUSB1"
 SUBSYSTEM=="tty"
 DRIVER==""

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1':
 KERNELS=="ttyUSB1"
 SUBSYSTEMS=="usb-serial"
 DRIVERS=="option1"
 ATTRS{port_number}=="0"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0':
 KERNELS=="4-1.1:1.0"
 SUBSYSTEMS=="usb"
 DRIVERS=="option"
 ATTRS{bInterfaceClass}=="ff"
 ATTRS{bInterfaceSubClass}=="ff"
 ATTRS{bInterfaceProtocol}=="ff"
 ATTRS{bNumEndpoints}=="02"
 ATTRS{supports_autosuspend}=="1"
 ATTRS{bAlternateSetting}==" 0"
 ATTRS{bInterfaceNumber}=="00"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1':
 KERNELS=="4-1.1"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="00"
 ATTRS{devpath}=="1.1"
 ATTRS{idVendor}=="1bc7"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 8"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="11"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="500mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="80"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="0"
 ATTRS{bcdDevice}=="0232"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{serial}=="0123456789ABCDEF"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="194649"
 ATTRS{ltm_capable}=="no"
 ATTRS{manufacturer}=="Android"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="1201"
 ATTRS{bDeviceClass}=="00"
 ATTRS{product}=="Android"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1':
 KERNELS=="4-1"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="01"
 ATTRS{devpath}=="1"
 ATTRS{idVendor}=="8087"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 1"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="2"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="0mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="e0"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="8"
 ATTRS{bcdDevice}=="0000"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="204"
 ATTRS{ltm_capable}=="no"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="0024"
 ATTRS{bDeviceClass}=="09"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4':
 KERNELS=="usb4"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="00"
 ATTRS{devpath}=="0"
 ATTRS{idVendor}=="1d6b"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 1"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{authorized_default}=="1"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="1"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="0mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="e0"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="2"
 ATTRS{bcdDevice}=="0316"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{serial}=="0000:00:1d.0"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="24"
 ATTRS{ltm_capable}=="no"
 ATTRS{manufacturer}=="Linux 3.16.0-4-amd64 ehci_hcd"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="0002"
 ATTRS{bDeviceClass}=="09"
 ATTRS{product}=="EHCI Host Controller"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0':
 KERNELS=="0000:00:1d.0"
 SUBSYSTEMS=="pci"
 DRIVERS=="ehci-pci"
 ATTRS{irq}=="23"
 ATTRS{subsystem_vendor}=="0x1458"
 ATTRS{broken_parity_status}=="0"
 ATTRS{class}=="0x0c0320"
 ATTRS{companion}==""
 ATTRS{driver_override}=="(null)"
 ATTRS{consistent_dma_mask_bits}=="32"
 ATTRS{dma_mask_bits}=="32"
 ATTRS{local_cpus}=="00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff"
 ATTRS{device}=="0x1c26"
 ATTRS{uframe_periodic_max}=="100"
 ATTRS{enable}=="1"
 ATTRS{msi_bus}==""
 ATTRS{local_cpulist}=="0-7"
 ATTRS{vendor}=="0x8086"
 ATTRS{subsystem_device}=="0x5006"
 ATTRS{numa_node}=="-1"
 ATTRS{d3cold_allowed}=="1"

 looking at parent device '/devices/pci0000:00':
 KERNELS=="pci0000:00"
 SUBSYSTEMS==""
 DRIVERS==""

After I got the info, I noticed that the one thing that was different between all of the ttyUSB devices was ATTRS{bInterfaceNumber}.  So, I wrote a rule that looked like this:

ATTRS{idProduct}=="1201", ATTRS{idVendor}=="1bc7", ATTRS{bInterfaceNumber}=="00", SYMLINK+="cellular_1"

However, this did not produce the expected result of a new symlink.  It appears as though you can’t get the udev information for a product/vendor and the interface number in the same rule.  However, there are environment variables that are set which you can use.  If you run udevadm test, you can see what exactly the environment variables are that are set:

$ udevadm test --action=add `udevadm info -q path -n /dev/ttyUSB1`

...skip a bunch of data...
ACTION=add
DEVLINKS=/dev/serial/by-id/usb-Android_Android_0123456789ABCDEF-if00-port0 /dev/serial/by-path/pci-0000:00:1d.0-usb-0:1.1:1.0-port0
DEVNAME=/dev/ttyUSB1
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1/tty/ttyUSB1
ID_BUS=usb
ID_MODEL=Android
ID_MODEL_ENC=Android
ID_MODEL_ID=1201
ID_PATH=pci-0000:00:1d.0-usb-0:1.1:1.0
ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_1_1_0
ID_REVISION=0232
ID_SERIAL=Android_Android_0123456789ABCDEF
ID_SERIAL_SHORT=0123456789ABCDEF
ID_TYPE=generic
ID_USB_DRIVER=option
ID_USB_INTERFACES=:ffffff:ff4201:ff0000:080650:
ID_USB_INTERFACE_NUM=00
ID_VENDOR=Android
ID_VENDOR_ENC=Android
ID_VENDOR_FROM_DATABASE=Telit Wireless Solutions
ID_VENDOR_ID=1bc7
MAJOR=188
MINOR=1
SUBSYSTEM=tty
TAGS=:systemd:
USEC_INITIALIZED=91898933

These variables can also be used in udev rules to do differentiate between different devices.  The two that we are interested in are ID_VENDOR_ID and ID_MODEL_ID, as these correspond to ATTRS{idProduct} and ATTRS{idVendor}.  Our new rule becomes:

DRIVERS=="option", ATTRS{bInterfaceNumber}=="00", ENV{ID_MODEL_ID}=="1201", ENV{ID_VENDOR_ID}=="1bc7", SYMLINK+="cellular_1"

The DRIVERS part of the rule may not be strictly necessary, but it can’t hurt it.

Feb 03

Web Design and Programmers

On a project at work, we’re starting into web development from being entirely desktop applications.  It’s a new thing to learn because of that.  We’re working with a contractor, who is creating the majority of the website for us.  He has suggested, and we have implemented, a REST-based service for sending data to and from the server.  Fortunately, making a REST-based web service in Java is very easy using Genson and JAX-RS.

However, this post is not about that.  It is about web technologies in general.  Originally, I spent a fair amount of time researching different web technologies that use Java as the backend, and I came up with a few such as JSP, JSF, as well as some frameworks, such as PrimeFaces.  We’re not using JSF technology at the moment, but that’s not really the point either.

There are a lot of different web technologies out there, and each has its own pros and cons.  My real question here is why do we treat web design as separate from programming the website?  While researching the advantages/disadvantages of a REST-only website, I came across this good post on programmers.SE about the pros and cons, which then led me to this interesting post about using tag libraries in web pages.  Essentially, what this boils down to is:

The benefit here is that web designers are supposed to be able to work with these tags, or at least that’s what they say. In practice, in all my years of software development and programming, I have yet to ever see this magical unicorn walk out of the shadows and into the sunlight.

My question here though is: why are we treating web development in a different light than we are treating desktop application development?

When developing a desktop application, generally the programmers are the people who create the design.  Even if somebody creates the design in a GUI designer, the programmer has to be involved in how to tie all the events together – they don’t exist in separate paradigms, which is what web technologies seem to do.

In fact, I make all of my Java GUIs by hand.  I have never made a QT gui by hand, but from the generated code that I have looked at it follows a very similar programming model(composite design pattern).  What exactly is preventing a web designer from doing code, and what is preventing a programmer from making a website?

It seems to me that this is not something where the programmer and the web designer are two different people.  What seems to be better would be to have people that can straddle both areas.  After all, we are generally not pigeon-holed into just one area.  If you work mostly on backend code, it is not unreasonable to also touch the GUI code at some point, so you must know how the GUIs are set-up.  But for web development, this seems to be just the opposite.

Perhaps what we need is a swing-like web framework that you don’t need to make any HTML files for, just create the entire website through code.  This sounds complicated, and I don’t know if it would solve the problem.

Jan 19

PIC32 and I2C

I’ve been struggling with Microchip’s plib library trying to get I2C working.  First, the example I2C code is horrible.  Second, I’m not even sure what sample hardware from Microchip actually uses it.  It’s just so confusing.

Anyway, there are a few things to note here about the I2C library and the quirks that it has:

  • Make sure that you have pull-up resistors on the I2C bus.  This was problem #1.
  • Make sure you configure all pins as digital, if they are analog pins.  This was problem #2.
  • There are two methods you need to acknowledge the I2C: I2CAcknowledgeByte and I2CByteWasAcknowledged.
  • When reading a byte from the bus when you are the master, you need to call I2CReceiverEnable for each byte that you need to get.

These functions are not very special; the problem is that they are poorly documented.  For the most part, the plib functions simply access the registers directly in memory and return an appropriate value after checking the bits that may be set as a side effect.  It’s not particularly hard, they are just oddly named often times.

Anyway, you can see more about this both here and… some other place on the Microchip forums, I can’t find it now.

Hope that helps.

Dec 22

Adventures in accidental chown

So earlier today I had to get Virtualbox up and running, and it was complaining about permissions not being set right.  For some reason, the permissions on my /usr directory were slightly messed up and not owned by root.  So, I did:

sudo chown root:root /usr

That didn’t fix the problem.  Going into the /usr directory, I also noticed that a few of the other folders weren’t owned by root.  So, let’s just go fix everything at once:

sudo chown -R root:root /usr

Okay, now let’s try starting Virtualbox!

Virtualbox now complains about not being setuid.  Okay, fine.  Let’s reinstall virtualbox, since it says that should fix it.

sudo dpkg -i virtualbox.deb
sudo: must be setuid root

Oops.

 

Turns out, doing the chroot dropped the setuid bit from all binaries in the /usr directory.

To fix this, you’ll have to boot from a rescue disk/CD of some kind, and change the permissions on sudo:

chmod 4755 /path/to/mounted/filesystem/usr/bin/sudo

Other programs will also be messed up at that point, but now that you have sudo you can change the permissions to what they should be, and/or reinstall. 🙂

Oct 29

The Definition of Programming

Programming (noun): The art of putting together software that other people have written and breaking it in new and exciting ways.

 

It’s rare that you will have to create something entirely new from scratch.  So, it’s always a matter of taking things that have already been made and putting them together.

Conceptually, this a pretty simple idea.  In practice however, it’s much more complicated.  You need to know how exactly all the parts come together.  This is why good documentation of libraries is very important.  Without good documentation, it’s next to impossible to integrate a needed library.  You almost have to become an expert in how the library works, which rather defeats the purpose of using a library in the first place! This is very annoying.  Please, write good documentation!

 

Also, it seems that ‘programming’ could be both a noun and a verb, but Webster’s only defines it as a noun.  Seems like a bit of an oversight.

Aug 11

GNOME 3 Plugins: Settings

This post is going to be based partially on the source of Coverflow Alt Tab, plus some of the source files that are already included in GNOME 3.

Also, a few notes here: it appears at this time that there is no way to easily import your schema into the GSettings backend.  This may be added at a future time.  So, any settings that you make will essentially be unstructured and possibly break if somebody messes around with them.

The first thing for us to do is to create a new XML schema for us. This schema holds the keys to our values, as well as default values and descriptions.  There’s some documentation on the GNOME website, so i will only go over a small portion of what is there, given that I only understand a small portion. 🙂

For our purposes, we will define two keys: one string key which will be the text to display, and a boolean value that we will mess around with later.  For now, let’s define the XML schema:

<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
  <schema id="com.rm5248.testplugin" path="/org/gnome/shell/extensions/rm5248/">
    <key type="s" name="hello-text" >
        <default>"Hello, World!"</default>
        <summary>The text to display</summary>
        <description>The text to display</description>
    </key>
    <key type="b" name="wait" >
        <default>false</default>
        <summary>Wait?</summary>
        <description>Do you wait until OK?</description>
    </key>
  </schema>
</schemalist>

This is pretty straightforward: you have a schema with a name(com.rm5248.testplugin) and two keys, a string value called ‘hello-text’ and a boolean vaue called ‘wait’.  Once we have written the XML file, we need to generate the binary version of it.  To do that, simply use the glib-compile-schemas tool:

glib-compile-schemas schemas/

Now that we have our compiled schema, it’s time to retrieve settings from the GSettings database.  In order to do that, we will need this lib.js file from CoverflowAltTab.  From the copyright, it appears to be from the GNOME sources, however I have been unable to find where it originally comes from.

Now that we have our schema compiled and in the schemas/ directory, it’s a very simple process to get the default settings(as we haven’t changed them at all.).  In fact, here’s the entire code:

const Gio = imports.gi.Gio;
const ThisExtension = imports.misc.extensionUtils.getCurrentExtension();
const Lib = ThisExtension.imports.lib;

const RM_SCHEMA = 'com.rm5248.testplugin';
const TEXT_KEY = 'hello-text';
const BOOL_KEY = 'wait';

let settings = null;

function init() {
    // Create the new settings
    settings = Lib.getSettings( RM_SCHEMA );
}

function enable() {
    log( settings.get_string( TEXT_KEY ) );
    log( settings.get_boolean( BOOL_KEY ) );
}

function disable() {
}

For now, we’re just printing out the settings to the console, which is not very useful. We’ll expand this in a bit, but before we do that let’s make a GTK widget to set the settings.  Looking at the GNOME documentation, we see that we can change settings by having a prefs.js file with a method buildPrefsWidget.

A quick note before we get started here: when constructing a C object, the parameters to the object(in the {} syntax) are properties to set on the object.  Take for example the properties on GTKLabel: if you want to set the ‘angle’ property when constructing, you would do:

new Gtk.Label( { angle: 20 } );

Do this for all the properties that you wish to set when you create the object.

Now, moving on to the actual code of the prefs.js file.  Everything here is fairly straightforward if you’ve done GTK+ programming before.  Here’s the code that will pop up a GTK+ widget:

function buildPrefsWidget(){
    let frame = new Gtk.Box( { orientation: Gtk.Orientation.VERTICAL, border_width: 10, spacing: 10 } );

    //There are two settings, so we will have one that changes the text and one that changes
    //the boolean value.
    //Create an EntryBuffer to store the text in
    let entryBuffer = new Gtk.EntryBuffer( { text: settings.get_string( TEXT_KEY ) } );
    let textBox = new Gtk.Box( { orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 } );
    let textLabel = new Gtk.Label( { label: "Hello text", xalign: 0 } );
    let textEntry = new Gtk.Entry( { buffer: entryBuffer } );
    //Gtk.Entry implements GtkEditable interface, which is where the 'changed' signal comes from
    textEntry.connect( 'changed', function( widget ){
      settings.set_string( TEXT_KEY, widget.get_text() );
     } );
    textBox.pack_start( textLabel, true, true, 0 );
    textBox.add( textEntry );

    //Let's change our boolean value now
    let boolBox = new Gtk.Box( { orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 } );
    let boolLabel = new Gtk.Label( { label: "Do we wait?", xalign: 0 } );
    let boolRadio = new Gtk.Switch( { active: settings.get_boolean( BOOL_KEY ) } );
    //Gtk.Switch has an 'activate' signal, but don't connect to that directly.
    //Instead, the docs say to use the 'notify::active' signal
    boolRadio.connect( 'notify::active', function( widget ){
        settings.set_boolean( BOOL_KEY, widget.get_active() );
     } );
    boolBox.pack_start( boolLabel, true, true, 0 );
    boolBox.add( boolRadio );

    frame.add( textBox );
    frame.add( boolBox );
    frame.show_all();

    return frame;
}

To test this out, re-load the shell (ALT+F2, r, enter), and open up gnome-tweak-tool.  Under ‘Extensions’, there should now be a small cog for the ‘settings example’ extension.  If you open that up, you should see the two settings that we made: one for the text, and one for the boolean value.  By changing these values, you can change what the extension will print out when the plugin is loaded.

You can also use dconf to see the values directly, once you have saved them first.  Change a value with gnome-tweak-tool, open up dconf and navigate to /org/gnome/shell/extensions/rm5248/, and you will see the settings that you have made.  Remember though, they only appear there if they are different from the defaults; this may not be true for all programs, but it is true for gnome-shell extensions.

You may find the complete source here.