5

Cross-platform web UI for C and Go

 3 years ago
source link: https://zserge.com/posts/webview/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Cross-platform web UI for C and Go

Cross-platform GUI has always been a painful part of software development.

Today the biggest horror for an old-school C developer is to look at the list of Electron apps on their website. Simple, often trivial utilities, each taking over a hundred of megabytes of disk space and consuming hundreds of RAM once launched.

I’m not going to start a rant here, people of HackerNews have said enough on this topic. Electron wins because it is easy to use and everyone knows how to write HTML/CSS (unlike, say, QML or XAML).

Still, there must be a better way. Even though RAM and disk space are cheap, it just doesn’t feel right.

I obviously won’t be the one to fix it with a silver bullet, but at least I can try. My today’s Sunday morning exercise was to make the smallest possible wrapper for various WebView implementations on Linux, MacOS and Windows and turn it into a cross-platform native GUI library that uses HTML/CSS for rendering.

Webviews

Let’s start with macOS. It comes bundled with a nice WebKit library, and that WebView can be easily integrated into a Cocoa app. No troubles here. Linux is a bit on a darker side. From what I’m aware of, modern Debian-based distributions don’t come with WebKit library preinstalled. However, due to the simplicity of package management and the smartness of an average Linux user, it should not be a problem to install gtk-webkit manually.

Windows, in contrast, is a total disaster. There is MSHTML and there is EdgeHTML. The first one is our dearest Internet Explorer, and the latter doesn’t have any APIs to embed it from the C code.

After spending some time trying to learn about UWP and EdgeHTML, I ended up embedding MSHTML via OLE, like in the old days. Surprisingly, it gives rather satisfactory results.

On Windows 8 user will end up with IE10, which is still broken, but at least supports most of the CSS3/HTML5. On Windows 10 it will be IE11, which is even better. But poor Windows 7 users will be enjoying IE7 in all its glory and there is not much we can do about it. And I don’t want to even start talking about Windows XP…

Of course it would be nice to have an advanced API with a flexible event loop and functions to control every aspect of a web view and the hosting window, but that’s beyond the time limits of a Sunday morning.

The library is available at https://github.com/zserge/webview

So I simplified the API down to one function call:

int webview(const char *title, const char *url,
	int width, int height, int resizable);

It is supposed to open a window with the given title, width and height (optinally - resizable) and render a full size web page with the given URL.

There is no way to provide actual HTML contents, but on Mac and Linux you can pass data:text/html,<html>....</html> instead of a URL.

Usage

The whole library is just a single header file of ~700 LOC and is available on github. It’s written in C99, so it should play nicely with other languages if you want to make bindings. I expect it to be used only for web UI rendering. I don’t force you to use any specific JS frameworks or communication methods with the main application core.

I would probably use a WebSocket for communication with the “backend” part of the app. If you can afford sharing you code under a GPL license - you can use Mongoose web framework, it supports WebSockets.

But in 2017 I would also consider using Go.

From C to Go

Of course, there is little fun in writing web apps in C. So I made a tiny cgo wrapper that allows opening a webview from Go.

A self-contained native app with webUI can look like this. It shows a greeting with the current user name and exits if you click the button. You can check the screenshots on the project github page.

package main

import (
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"os/user"

	"github.com/zserge/webview"
)

var tmpl = template.Must(template.New("").Parse(`
<html>
	<head>
		<style>
		* { margin: 0; padding: 0; box-sizing: border-box; font-family: Helvetica, Arial, sans-serif; }
		body { color: #ffffff; background-color: #03a9f4; text-decoration: uppercase; font-size: 24px; }
		h1 { text-align: center; font-weight: normal}
		form { margin-left: auto; margin-right: auto; margin-top: 50px; width: 300px; }
		input[type="submit"] {
				border: 0 none;
				cursor: pointer;
				margin-top: 1em;
				background-color: #ffffff;
				color: #03a9f4;
				width: 100%;
				height: 2em;
				font-size: 24px;
				text-transform: uppercase;
		}
		</style>
	</head>
	<body>
	  <form action="/exit">
			<h1>Hello, {{ markdownhack }}!</h1>
			<input type="submit" value="Exit" />
		</form>
	</body>
</html>
`))

func main() {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()

	go func() {
		http.HandleFunc("/exit", func(w http.ResponseWriter, r *http.Request) {
			os.Exit(0)
		})
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			u, _ := user.Current()
			tmpl.Execute(w, u)
		})
		log.Fatal(http.Serve(ln, nil))
	}()

	webview.Open("Hello", "http://"+ln.Addr().String(), 400, 300, false)
}

The good thing is that unlike many Electron apps, this demo consumes only ~6MB of RAM and the binary size is about 7MB unstripped (I’ve only measured it on Linux).

Of course, it’s a very early proof-of-concept and I haven’t used it in any serious apps yet. But the whole idea of running a native web UI on top of a memory safe modern language is very appealing to me. So if you share my views, or found a bug, or have a suggestion to make - feel free to contribute!

I hope you’ve enjoyed this article. You can follow – and contribute to – on Github, Twitter or subscribe via rss.

Aug 20, 2017


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK