Developing Window Managers
26 Jul 2014I wanted to use this post to jot down a few notes and resources for developing window managers for the X Windows system.
I wanted to use this post to jot down a few notes and resources for developing window managers for the X Windows system.
Today’s post isn’t quite a normal post. I’m going to use this as a place to put all of my notes while using emacs.
Key | Description |
---|---|
C-v |
Page down |
M-v |
Page up |
C-l |
Move text around the cursor (center, top, bottom) |
C-p |
Previous line |
C-n |
Next line |
C-b |
Backwards character |
C-f |
Forward character |
M-b |
Backwards word |
M-f |
Forward word |
C-a |
Beginning of line |
C-e |
End of line |
M-a |
Beginning of sentence |
M-e |
End of sentence |
M-< |
Beginning of text |
M-> |
End of text |
Key | Description |
---|---|
<DEL> |
Delete character before cursor |
C-d |
Delete character after cursor |
M-<DEL> |
Kill word before cursor |
M-d |
Kill word after cursor |
C-k |
Kill from cursor to EOL |
M-k |
Kill to the end of current sentence |
C-<SPC> |
Start selecting text |
C-x h |
Select the whole buffer |
C-w |
Kill selection |
M-w |
Save region but don’t kill it |
C-y |
Yank killed text |
M-y |
Cycle through previous kills |
C-/ |
Undo |
Key | Description |
---|---|
C-g |
Exit current command |
C-u |
Repeat command |
M-x replace-string |
Find and replace |
M-x recover-file |
Recover file backup |
C-M-\ |
Indent selected region |
Key | Description |
---|---|
C-x C-f |
Find a file (for opening) |
C-x C-s |
Save current file |
C-x C-b |
List buffers |
C-x b |
Switch to a buffer |
C-x s |
Save some buffers |
C-x C-c |
Quit emacs (prompt for saves) |
C-x k |
Kill buffer |
Key | Description |
---|---|
C-x 1 |
Delete all but current window |
C-x 2 |
Split window horizontally |
C-x 3 |
Split window verticaly |
C-x 4 C-f |
Find a file into a new window |
C-x o |
Move to other window |
C-M-v |
Scroll bottom window |
M-x make-frame |
Create a new emacs frame |
M-x delete-frame |
Remove the selected emacs frame |
Key | Description |
---|---|
M-x fundamental-mode |
Fundamental |
M-x text-mode |
Human text mode |
Key | Description |
---|---|
C-h m |
Documentation on current major mode |
C-h ? |
Get help on what you can get help on |
C-h c |
Get help on a command |
C-h f |
Get help on a function |
C-h a |
List commands by keyword |
C-h i |
Open manuals (open info buffer) |
Key | Description |
---|---|
C-s |
Start forward incremental search |
C-r |
Start backward incremental search |
When searching forwards, C-s
will take you through all of the occurences of the search term that has been found. <DEL>
takes you backwards. <DEL>
starts to effect the search term once you’ve reached the first result.
Key | Description |
---|---|
M-x dired |
Open dired in a buffer |
o |
Open the highlighted file in the other buffer |
C-o |
Open the file but keep focus in the dired buffer |
After a fresh installation of FreeBSD today, I’d noticed that my boot up time was suffering due to this message which consistently appears:
Jul 19 16:58:38 freebsd sm-mta[1097]: My unqualified host name (freebsd) unknown; sleeping for retry
Jul 19 16:59:38 freebsd sm-mta[1097]: unable to qualify my own domain name (freebsd) -- using short name
Jul 19 16:59:38 freebsd sm-msp-queue[1100]: My unqualified host name (freebsd) unknown; sleeping for retry
Jul 19 17:00:38 freebsd sm-msp-queue[1100]: unable to qualify my own domain name (freebsd) -- using short
As you can see, this machine that I’ve created does have the very original hostname of freebsd.
What the error message is telling us is that I need to fully qualify my hostname. Editing /etc/rc.conf you can change the hostname
value to include this information. The top line of my rc.conf now reads as follows:
hostname="freebsd.home"
No more slow boot times; because of this problem, at least.
Getting direct access over ssh is simplified greatly by sshfs, a fuse based file system. To get started, install sshfs with your favourite package manager:
$ sudo pacman -S sshfs
To connect to a remote file system, you just use the following:
$ sshfs host: mountpoint
Much like ssh, the host
argument can take on the format of user@host if you’re logged in as a user that doesn’t correspond to the remote machine.
When you’re done, unmounting the filesystem is done like so:
$ fusermount -u mountpoint
For Intel chips, the major processes (like memory management, interrupts, etc) are managed through a set of tables. These tables are as simple as length and a linear address to the actual table data.
The GDT or Global Descriptor Table is one of these tables and it’s what your CPU uses to describe its internal memory segmentation for the system.
In today’s post, I’ll take you through how the GDT is defined and how it is applied to your system.
Like I said in the introduction, all that the GDT is made up of is a length and a linear address to the data. Here’s an example below, defined in assembly language
gdt_data:
DQ 0x0000000000000000
DQ 0x00CF9A000000FFFF
DQ 0x00CF92000000FFFF
gdt_tab:
DW 23
DD gdt_data
In the above snippet, gdt_data
defines the actual GDT entries. We’ll get into what the values mean shortly, but for now it’s important to understand that this block of data starts with a null entry (or all zeros) and then the entries begin. You’ll see that each entry is defined by DQ
, so each entry is 8 bytes.
gdt_tab
starts with the length of the structure (minus 1). The whole “minus 1” part comes in because the expected data type of the length is a word, it can only hold a maximum value of 65535 but you are allowed up to 65536 entries in the table. Obviously, it’s invalid to specify a table that has a zero length. Next, gdt_tab
defines a linear address to the table data itself, gdt_data
.
Each GDT entry conforms to the following format:
Start | End | Meaning | Size |
---|---|---|---|
63 | 56 | Base (bits 24 - 31) | 8 bits |
55 | 52 | Flags | 4 bits |
51 | 48 | Limit (bits 16 - 19) | 4 bits |
47 | 40 | Access byte | 8 bits |
39 | 16 | Base (bits 0 - 23) | 24 bits |
15 | 0 | Limit (bits 0 - 15) | 16 bits |
From this table, you can see that it defines a 32 bit base which is a linear address of where the segment begins and a 20 bit limit which is the maximum addressable unit.
The access byte is 8 bits in flags that describe different access privileges. The byte breaks down like this:
Bit | Code | Description |
---|---|---|
7 | Pr | Present bit. Must be 1 for all selectors. |
6-5 | Privl | Privilege bits. Defines the ring level this selector is allowed to be used from. |
4 | Always 1 | |
3 | Ex | Executable bit. 1 for code, 0 for data |
2 | DC | Direction bit/Conforming bit. This is a direction bit for data selectors, in which case when it is set to 0, the segment grows up. 1, it’ll grow down. This is a conforming bit for code selectors. When is is set to 1, execution is allowed by the defined privilege level or below. When it’s 0, it’s only allowed from the defined privilege level. |
1 | RW | Readable for code selectors, Writeable for data selectors. Code selectors can’t have write access and data selectors don’t have read access. |
0 | Ac | Leave this as 0. The CPU will set it to 1 once the segment is accessed |
The flags nibble is 4 bits that control size:
Bit | Code | Description |
---|---|---|
7 | Gr | Granularity when set to 0 will make the limit be interpreted in bytes. When it’s set to 1, the limit is defined in pages (4KiB blocks) |
6 | Sz | Size when 0 defines 16-bit protected mode. 1 defines as 32-bit mode selectors |
5 | L | Long when set to 1 will setup 64-bit mode selectors. Sz must be set to 0 |
4 | Unused. Set to 0 |
Above, we had some example data that we were setting up for a GDT. Here’s how those values break down.
Original value
00CF9A000000FFFF
base 24-31 : 00
flags : C (1100b)
limit 16-19 : F
access : 9A (10011010b)
base 23-0 : 000000
limit 15-0 : FFFF
This particular entry says it’s at a base of 0x00000000, has a limit of 0xFFFFF. The access byte tells us that the segment is:
The flags also tell us that the segment has:
Actually defining the GDT entries is one thing, but you also need to set them as well. This is quite an easy process.
mov eax, gdt_tab ; load in the address of the table
lgdt [eax] ; load the new GDT
After this has happened, we need to jump into our new segment to continue executing code. In the table gdt_tab
, the code segment was defined 2nd (after the null entry). The code segment definition is 0x08
(or just 8) bytes into the table.
After jumping to our code segment, we need to refresh all of the segment selectors so that they’re now pointing at the right place as well. 16 bytes (0x10
) into the table (the third entry) is where we’ve defined the data segment.
jmp 0x08:refresh_segments
refresh_segments:
mov eax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
Segmentation is very simple once you enter the 64 bit world. Four of the segment registers: CS
, SS
, DS
and ES
start at 0x00
and have a limit of 0xFFFFFFFFFFFFFFFF
. Pretty simple. FS
and GS
are still capable of a non-zero base address.
An example table on how this would look is like this:
gdt_tab_64:
DQ 0x0000000000000000
DQ 0x00A09A0000000000
DQ 0x00A0920000000000
You can see how the base and limits have simplified greatly here.
There’s quite a bit more you can learn in this field. There’s also some excellent resources around the web to help out. Here’s just a few: