pygame, gopherspace, and window managers
Tags: showdev, python, pygame, gopherspace
Published: 2018-09-24 15:35:17

---
title: pygame, gopherspace, and window managers
published: true
description: How i wrote a virtual-window-manager-powered gopherspace client in python, using pygame.
tags: showdev,python,pygame,gopherspace
---

Some people may think pygame is mostly useful for games and multimedia, that's not all its useful for. Some people may also think you can't use multiple windows in pygame. While the latter is true, i managed to work around that.


#What is gopherspace
First off, I want to give a brief description of what gopherspace actually is. 

Way back before the web, in the year 1991, a new protocol emerged onto the internet. Gopher. Rather than being hyper text, or as crude as a FTP directory listing, gopher is kinda in-between.

Gopherspace is really just one big series of menus. No scripts, no more formatting than a plain .txt document, and no CSS.  Over the years, gopher slid into obsucurity. However, its still out there, in part to its absurdly simple protocol:

###Step 1:
client connects to server.

###Step 2:
client sends a 'selector' aka path of the desired document, a tap+query if its a search query, and a CR+LF.:

```
/some/document[CRLF]
/some/query[tab]some search terms[CRLF]
```

###Step 3:
Client waits for server to send the full document/file selected, or, if an error occurs, usually an error page containing item type '3'

##The Menus:
A gopher menu's synax probably couldn't be simpler (literally):
usually the port is 70. The first char of each line defines the item's type.
notice the 'i' type has a 'null' selector field, as its not supposed to be even selectable.

```
iI'm an information type[tab]null[tab]server[tab]port[CRLF]
1I'm a gopher menu somewhere[tab]/this/is/a/selector/[tab]server[tab]port[CRLF]
0I'm a text document somewhere[tab]/this/is/a/selector/also.txt[tab]server[tab]port[CRLF]
7I'm a text query (or index search) somewhere[tab]/some/search[tab]server[tab]port[CRLF]
```

a type '3' is returned upon an error of some kind. most commonly a nonexistent selector, which is akin to a 404 error.

There are plenty of other types and things, but that's the basics.

#how i got around pygame/sdl's 1-window limit (sort of ;D )

Now to the fun part. how do you run multiple windows, while you only have 1?
a virtual window manager.
{% github https://github.com/ThomasTheSpaceFox/Strazoloid-windowing-framework %}

I actually had already written the basic structure of strazoloid before working on zoxenpher, but it was greatly improved.

Starting up strazoloidwm is easy enough:
```python
import strazoloidwm as stz
desk=stz.desktop(800, 600, "This is the window title", pumpcall=event_test, resizable=1)
#init Framework
framesc=stz.framescape(desk)
#create new 'framex' object (virtual window)
testframe2=stz.framex(200, 100, "test2", pumpcall=event_test)
#add the frame (window) to our framescape instance.
framesc.add_frame(testframe2)
#hand over main thread to framescape instance, starting the window manager.
framesc.process()
```

whats `event_test` you might ask? well:

```python
def event_test(frameobj, data=None):
	#note: you should make sure all non if-statement code is behind if statements. see comments on frame/desktop flags below.
	if frameobj.statflg==1:
		print(frameobj.name + " stat 1: init call")
	elif frameobj.statflg==2:
		print(frameobj.name + " stat 2: post-resize call")
	elif frameobj.statflg==3:
		print(frameobj.name + " stat 3: frame terminate call. type:")
		if frameobj.runflg==0:
			print("   run 0: wm quit call")
			print(frameobj.pid)
			print(frameobj.wo)
		if frameobj.runflg==2:#the desktop class should never set runflg=2
			print("   run 2: frame (window) close call")
			print(frameobj.pid)
			print(frameobj.wo)
	elif frameobj.statflg==4:
		print(frameobj.name + " stat 4: clickdown")
	elif frameobj.statflg==5:
		print(frameobj.name + " stat 5: clickup")
	elif frameobj.statflg==6:
		print(frameobj.name + " stat 6: keydown")
	elif frameobj.statflg==7:
		print(frameobj.name + " stat 7: keyup")
	elif frameobj.statflg==8:
		print(frameobj.name + " stat 8: desktop window resize")
	elif frameobj.statflg==9:
		print(frameobj.name + " stat 9: frame shade")
	elif frameobj.statflg==10:
                print(frameobj.name + " stat 10: frame unshade")
```

Whats all the statflg checks? well. that's how strazoloidwm lets the program know, what reason frame/desktop object's 'pumpcall' was called.  Note that pumpcall can easily be a class instance method. 

You can find a full test program [here](https://github.com/ThomasTheSpaceFox/Strazoloid-windowing-framework/blob/master/stz-test.py).

#Zoxenpher
{% github https://github.com/ThomasTheSpaceFox/Zoxenpher %}
![Screenshot of zoxenpher](https://thepracticaldev.s3.amazonaws.com/i/5if5xunhqi8ua0o4wq7v.jpg)
Combining a custom gopher parser/access library, and some implementation-specific code, and the power of strazoloidwm, zoxenpher Is a neat gopher client sure,  i was able to get the basic functionality up and going quite quickly, as much of the grunt work was already handled by strazoloidwm.

Its complete with bookmarks, window-history navigation, images and even sound type support. plus using pygame allowed me to give it a bit of a retro-ish look that fits the protocol quite well i think.