+ buf[0] = '\0';
+ XFree(p);
+
+ return buf;
+}
+
+void
+updatetitle(Client *c)
+{
+ char *title;
+ const char *name = c->overtitle ? c->overtitle :
+ c->title ? c->title : "";
+
+ if (curconfig[ShowIndicators].val.i) {
+ gettogglestats(c);
+ getpagestats(c);
+
+ if (c->progress != 100)
+ title = g_strdup_printf("[%i%%] %s:%s | %s",
+ c->progress, togglestats, pagestats, name);
+ else
+ title = g_strdup_printf("%s:%s | %s",
+ togglestats, pagestats, name);
+
+ gtk_window_set_title(GTK_WINDOW(c->win), title);
+ g_free(title);
+ } else {
+ gtk_window_set_title(GTK_WINDOW(c->win), name);
+ }
+}
+
+void
+gettogglestats(Client *c)
+{
+ togglestats[0] = cookiepolicy_set(cookiepolicy_get());
+ togglestats[1] = curconfig[CaretBrowsing].val.i ? 'C' : 'c';
+ togglestats[2] = curconfig[Geolocation].val.i ? 'G' : 'g';
+ togglestats[3] = curconfig[DiskCache].val.i ? 'D' : 'd';
+ togglestats[4] = curconfig[LoadImages].val.i ? 'I' : 'i';
+ togglestats[5] = curconfig[JavaScript].val.i ? 'S' : 's';
+ togglestats[6] = curconfig[Plugins].val.i ? 'V' : 'v';
+ togglestats[7] = curconfig[Style].val.i ? 'M' : 'm';
+ togglestats[8] = curconfig[FrameFlattening].val.i ? 'F' : 'f';
+ togglestats[9] = curconfig[Certificate].val.i ? 'X' : 'x';
+ togglestats[10] = curconfig[StrictTLS].val.i ? 'T' : 't';
+ togglestats[11] = '\0';
+}
+
+void
+getpagestats(Client *c)
+{
+ if (c->https)
+ pagestats[0] = (c->tlserr || c->insecure) ? 'U' : 'T';
+ else
+ pagestats[0] = '-';
+ pagestats[1] = '\0';
+}
+
+WebKitCookieAcceptPolicy
+cookiepolicy_get(void)
+{
+ switch (((char *)curconfig[CookiePolicies].val.v)[cookiepolicy]) {
+ case 'a':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER;
+ case '@':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY;
+ default: /* fallthrough */
+ case 'A':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS;
+ }
+}
+
+char
+cookiepolicy_set(const WebKitCookieAcceptPolicy p)
+{
+ switch (p) {
+ case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER:
+ return 'a';
+ case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY:
+ return '@';
+ default: /* fallthrough */
+ case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS:
+ return 'A';
+ }
+}
+
+void
+seturiparameters(Client *c, const char *uri, ParamName *params)
+{
+ Parameter *config, *uriconfig = NULL;
+ int i, p;
+
+ for (i = 0; i < LENGTH(uriparams); ++i) {
+ if (uriparams[i].uri &&
+ !regexec(&(uriparams[i].re), uri, 0, NULL, 0)) {
+ uriconfig = uriparams[i].config;
+ break;
+ }
+ }
+
+ curconfig = uriconfig ? uriconfig : defconfig;
+
+ for (i = 0; (p = params[i]) != ParameterLast; ++i) {
+ switch(p) {
+ default: /* FALLTHROUGH */
+ if (!(defconfig[p].prio < curconfig[p].prio ||
+ defconfig[p].prio < modparams[p]))
+ continue;
+ case Certificate:
+ case CookiePolicies:
+ case Style:
+ setparameter(c, 0, p, &curconfig[p].val);
+ }
+ }
+}
+
+void
+setparameter(Client *c, int refresh, ParamName p, const Arg *a)
+{
+ GdkRGBA bgcolor = { 0 };
+ WebKitSettings *s = webkit_web_view_get_settings(c->view);
+
+ modparams[p] = curconfig[p].prio;
+
+ switch (p) {
+ case AcceleratedCanvas:
+ webkit_settings_set_enable_accelerated_2d_canvas(s, a->i);
+ break;
+ case AccessMicrophone:
+ return; /* do nothing */
+ case AccessWebcam:
+ return; /* do nothing */
+ case CaretBrowsing:
+ webkit_settings_set_enable_caret_browsing(s, a->i);
+ refresh = 0;
+ break;
+ case Certificate:
+ if (a->i)
+ setcert(c, geturi(c));
+ return; /* do not update */
+ case CookiePolicies:
+ webkit_cookie_manager_set_accept_policy(
+ webkit_web_context_get_cookie_manager(
+ webkit_web_view_get_context(c->view)),
+ cookiepolicy_get());
+ refresh = 0;
+ break;
+ case DiskCache:
+ webkit_web_context_set_cache_model(
+ webkit_web_view_get_context(c->view), a->i ?
+ WEBKIT_CACHE_MODEL_WEB_BROWSER :
+ WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+ return; /* do not update */
+ case DefaultCharset:
+ webkit_settings_set_default_charset(s, a->v);
+ return; /* do not update */
+ case DNSPrefetch:
+ webkit_settings_set_enable_dns_prefetching(s, a->i);
+ return; /* do not update */
+ case FileURLsCrossAccess:
+ webkit_settings_set_allow_file_access_from_file_urls(s, a->i);
+ webkit_settings_set_allow_universal_access_from_file_urls(s, a->i);
+ return; /* do not update */
+ case FontSize:
+ webkit_settings_set_default_font_size(s, a->i);
+ return; /* do not update */
+ case FrameFlattening:
+ webkit_settings_set_enable_frame_flattening(s, a->i);
+ break;
+ case Geolocation:
+ refresh = 0;
+ break;
+ case HideBackground:
+ if (a->i)
+ webkit_web_view_set_background_color(c->view, &bgcolor);
+ return; /* do not update */
+ case Inspector:
+ webkit_settings_set_enable_developer_extras(s, a->i);
+ return; /* do not update */
+ case Java:
+ webkit_settings_set_enable_java(s, a->i);
+ return; /* do not update */
+ case JavaScript:
+ webkit_settings_set_enable_javascript(s, a->i);
+ break;
+ case KioskMode:
+ return; /* do nothing */
+ case LoadImages:
+ webkit_settings_set_auto_load_images(s, a->i);
+ break;
+ case MediaManualPlay:
+ webkit_settings_set_media_playback_requires_user_gesture(s, a->i);
+ break;
+ case Plugins:
+ webkit_settings_set_enable_plugins(s, a->i);
+ break;
+ case PreferredLanguages:
+ return; /* do nothing */
+ case RunInFullscreen:
+ return; /* do nothing */
+ case ScrollBars:
+ /* Disabled until we write some WebKitWebExtension for
+ * manipulating the DOM directly.
+ enablescrollbars = !enablescrollbars;
+ evalscript(c, "document.documentElement.style.overflow = '%s'",
+ enablescrollbars ? "auto" : "hidden");
+ */
+ return; /* do not update */
+ case ShowIndicators:
+ break;
+ case SmoothScrolling:
+ webkit_settings_set_enable_smooth_scrolling(s, a->i);
+ return; /* do not update */
+ case SiteQuirks:
+ webkit_settings_set_enable_site_specific_quirks(s, a->i);
+ break;
+ case SpellChecking:
+ webkit_web_context_set_spell_checking_enabled(
+ webkit_web_view_get_context(c->view), a->i);
+ return; /* do not update */
+ case SpellLanguages:
+ return; /* do nothing */
+ case StrictTLS:
+ webkit_web_context_set_tls_errors_policy(
+ webkit_web_view_get_context(c->view), a->i ?
+ WEBKIT_TLS_ERRORS_POLICY_FAIL :
+ WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+ break;
+ case Style:
+ webkit_user_content_manager_remove_all_style_sheets(
+ webkit_web_view_get_user_content_manager(c->view));
+ if (a->i)
+ setstyle(c, getstyle(geturi(c)));
+ refresh = 0;
+ break;
+ case WebGL:
+ webkit_settings_set_enable_webgl(s, a->i);
+ break;
+ case ZoomLevel:
+ webkit_web_view_set_zoom_level(c->view, a->f);
+ return; /* do not update */
+ default:
+ return; /* do nothing */
+ }
+
+ updatetitle(c);
+ if (refresh)
+ reload(c, a);
+}
+
+const char *
+getcert(const char *uri)
+{
+ int i;
+
+ for (i = 0; i < LENGTH(certs); ++i) {
+ if (certs[i].regex &&
+ !regexec(&(certs[i].re), uri, 0, NULL, 0))
+ return certs[i].file;
+ }
+
+ return NULL;
+}
+
+void
+setcert(Client *c, const char *uri)
+{
+ const char *file = getcert(uri);
+ char *host;
+ GTlsCertificate *cert;
+
+ if (!file)
+ return;
+
+ if (!(cert = g_tls_certificate_new_from_file(file, NULL))) {
+ fprintf(stderr, "Could not read certificate file: %s\n", file);
+ return;
+ }
+
+ if ((uri = strstr(uri, "https://"))) {
+ uri += sizeof("https://") - 1;
+ host = g_strndup(uri, strchr(uri, '/') - uri);
+ webkit_web_context_allow_tls_certificate_for_host(
+ webkit_web_view_get_context(c->view), cert, host);
+ g_free(host);
+ }
+
+ g_object_unref(cert);
+
+}
+
+const char *
+getstyle(const char *uri)
+{
+ int i;
+
+ if (stylefile)
+ return stylefile;
+
+ for (i = 0; i < LENGTH(styles); ++i) {
+ if (styles[i].regex &&
+ !regexec(&(styles[i].re), uri, 0, NULL, 0))
+ return styles[i].file;
+ }
+
+ return "";
+}
+
+void
+setstyle(Client *c, const char *file)
+{
+ gchar *style;
+
+ if (!g_file_get_contents(file, &style, NULL, NULL)) {
+ fprintf(stderr, "Could not read style file: %s\n", file);
+ return;
+ }
+
+ webkit_user_content_manager_add_style_sheet(
+ webkit_web_view_get_user_content_manager(c->view),
+ webkit_user_style_sheet_new(style,
+ WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+ WEBKIT_USER_STYLE_LEVEL_USER,
+ NULL, NULL));
+
+ g_free(style);
+}
+
+void
+runscript(Client *c)
+{
+ gchar *script;
+ gsize l;
+
+ if (g_file_get_contents(scriptfile, &script, &l, NULL) && l)
+ evalscript(c, "%s", script);
+ g_free(script);
+}
+
+void
+evalscript(Client *c, const char *jsstr, ...)
+{
+ va_list ap;
+ gchar *script;
+
+ va_start(ap, jsstr);
+ script = g_strdup_vprintf(jsstr, ap);
+ va_end(ap);
+
+ webkit_web_view_run_javascript(c->view, script, NULL, NULL, NULL);
+ g_free(script);
+}
+
+void
+updatewinid(Client *c)
+{
+ snprintf(winid, LENGTH(winid), "%lu", c->xid);
+}
+
+void
+handleplumb(Client *c, const char *uri)
+{
+ Arg a = (Arg)PLUMB(uri);
+ spawn(c, &a);
+}
+
+void
+newwindow(Client *c, const Arg *a, int noembed)
+{
+ int i = 0;
+ char tmp[64];
+ const char *cmd[29], *uri;
+ const Arg arg = { .v = cmd };
+
+ cmd[i++] = argv0;
+ cmd[i++] = "-a";
+ cmd[i++] = curconfig[CookiePolicies].val.v;
+ cmd[i++] = curconfig[ScrollBars].val.i ? "-B" : "-b";
+ if (cookiefile && g_strcmp0(cookiefile, "")) {
+ cmd[i++] = "-c";
+ cmd[i++] = cookiefile;
+ }
+ if (stylefile && g_strcmp0(stylefile, "")) {
+ cmd[i++] = "-C";
+ cmd[i++] = stylefile;
+ }
+ cmd[i++] = curconfig[DiskCache].val.i ? "-D" : "-d";
+ if (embed && !noembed) {
+ cmd[i++] = "-e";
+ snprintf(tmp, LENGTH(tmp), "%lu", embed);
+ cmd[i++] = tmp;
+ }
+ cmd[i++] = curconfig[RunInFullscreen].val.i ? "-F" : "-f" ;
+ cmd[i++] = curconfig[Geolocation].val.i ? "-G" : "-g" ;
+ cmd[i++] = curconfig[LoadImages].val.i ? "-I" : "-i" ;
+ cmd[i++] = curconfig[KioskMode].val.i ? "-K" : "-k" ;
+ cmd[i++] = curconfig[Style].val.i ? "-M" : "-m" ;
+ cmd[i++] = curconfig[Inspector].val.i ? "-N" : "-n" ;
+ cmd[i++] = curconfig[Plugins].val.i ? "-P" : "-p" ;
+ if (scriptfile && g_strcmp0(scriptfile, "")) {
+ cmd[i++] = "-r";
+ cmd[i++] = scriptfile;
+ }
+ cmd[i++] = curconfig[JavaScript].val.i ? "-S" : "-s";
+ cmd[i++] = curconfig[StrictTLS].val.i ? "-T" : "-t";
+ if (fulluseragent && g_strcmp0(fulluseragent, "")) {
+ cmd[i++] = "-u";
+ cmd[i++] = fulluseragent;
+ }
+ if (showxid)
+ cmd[i++] = "-w";
+ cmd[i++] = curconfig[Certificate].val.i ? "-X" : "-x" ;
+ /* do not keep zoom level */
+ cmd[i++] = "--";
+ if ((uri = a->v))
+ cmd[i++] = uri;
+ cmd[i] = NULL;
+
+ spawn(c, &arg);
+}
+
+void
+spawn(Client *c, const Arg *a)
+{
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ close(pipein[0]);
+ close(pipeout[1]);
+ setsid();
+ execvp(((char **)a->v)[0], (char **)a->v);
+ fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]);
+ perror(" failed");
+ exit(1);
+ }
+}
+
+void
+destroyclient(Client *c)
+{
+ Client *p;
+
+ webkit_web_view_stop_loading(c->view);
+ /* Not needed, has already been called
+ gtk_widget_destroy(c->win);
+ */
+
+ for (p = clients; p && p->next != c; p = p->next)
+ ;
+ if (p)
+ p->next = c->next;
+ else
+ clients = c->next;
+ free(c);
+}
+
+void
+cleanup(void)
+{
+ while (clients)
+ destroyclient(clients);
+
+ close(pipein[0]);
+ close(pipeout[1]);
+ g_free(cookiefile);
+ g_free(scriptfile);
+ g_free(stylefile);
+ g_free(cachedir);
+ XCloseDisplay(dpy);
+}
+
+WebKitWebView *
+newview(Client *c, WebKitWebView *rv)
+{
+ WebKitWebView *v;
+ WebKitSettings *settings;
+ WebKitWebContext *context;
+ WebKitCookieManager *cookiemanager;
+ WebKitUserContentManager *contentmanager;
+
+ /* Webview */
+ if (rv) {
+ v = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(rv));
+ } else {
+ settings = webkit_settings_new_with_settings(
+ "allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i,
+ "allow-universal-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i,
+ "auto-load-images", curconfig[LoadImages].val.i,
+ "default-charset", curconfig[DefaultCharset].val.v,
+ "default-font-size", curconfig[FontSize].val.i,
+ "enable-caret-browsing", curconfig[CaretBrowsing].val.i,
+ "enable-developer-extras", curconfig[Inspector].val.i,
+ "enable-dns-prefetching", curconfig[DNSPrefetch].val.i,
+ "enable-frame-flattening", curconfig[FrameFlattening].val.i,
+ "enable-html5-database", curconfig[DiskCache].val.i,
+ "enable-html5-local-storage", curconfig[DiskCache].val.i,
+ "enable-java", curconfig[Java].val.i,
+ "enable-javascript", curconfig[JavaScript].val.i,
+ "enable-plugins", curconfig[Plugins].val.i,
+ "enable-accelerated-2d-canvas", curconfig[AcceleratedCanvas].val.i,
+ "enable-site-specific-quirks", curconfig[SiteQuirks].val.i,
+ "enable-smooth-scrolling", curconfig[SmoothScrolling].val.i,
+ "enable-webgl", curconfig[WebGL].val.i,
+ "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.i,
+ NULL);
+/* For more interesting settings, have a look at
+ * http://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html */
+
+ if (strcmp(fulluseragent, "")) {
+ webkit_settings_set_user_agent(settings, fulluseragent);
+ } else if (surfuseragent) {
+ webkit_settings_set_user_agent_with_application_details(
+ settings, "Surf", VERSION);
+ }
+ useragent = webkit_settings_get_user_agent(settings);
+
+ contentmanager = webkit_user_content_manager_new();
+
+ context = webkit_web_context_new_with_website_data_manager(
+ webkit_website_data_manager_new(
+ "base-cache-directory", cachedir,
+ "base-data-directory", cachedir,
+ NULL));
+
+ cookiemanager = webkit_web_context_get_cookie_manager(context);
+
+ /* rendering process model, can be a shared unique one
+ * or one for each view */
+ webkit_web_context_set_process_model(context,
+ WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
+ /* TLS */
+ webkit_web_context_set_tls_errors_policy(context,
+ curconfig[StrictTLS].val.i ? WEBKIT_TLS_ERRORS_POLICY_FAIL :
+ WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+ /* disk cache */
+ webkit_web_context_set_cache_model(context,
+ curconfig[DiskCache].val.i ? WEBKIT_CACHE_MODEL_WEB_BROWSER :
+ WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+
+ /* Currently only works with text file to be compatible with curl */
+ webkit_cookie_manager_set_persistent_storage(cookiemanager,
+ cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
+ /* cookie policy */
+ webkit_cookie_manager_set_accept_policy(cookiemanager,
+ cookiepolicy_get());
+ /* languages */
+ webkit_web_context_set_preferred_languages(context,
+ curconfig[PreferredLanguages].val.v);
+ webkit_web_context_set_spell_checking_languages(context,
+ curconfig[SpellLanguages].val.v);
+ webkit_web_context_set_spell_checking_enabled(context,
+ curconfig[SpellChecking].val.i);
+
+ g_signal_connect(G_OBJECT(context), "download-started",
+ G_CALLBACK(downloadstarted), c);
+ g_signal_connect(G_OBJECT(context), "initialize-web-extensions",
+ G_CALLBACK(initwebextensions), c);
+
+ v = g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "settings", settings,
+ "user-content-manager", contentmanager,
+ "web-context", context,
+ NULL);
+ }
+
+ g_signal_connect(G_OBJECT(v), "notify::estimated-load-progress",
+ G_CALLBACK(progresschanged), c);
+ g_signal_connect(G_OBJECT(v), "notify::title",
+ G_CALLBACK(titlechanged), c);
+ g_signal_connect(G_OBJECT(v), "button-release-event",
+ G_CALLBACK(buttonreleased), c);
+ g_signal_connect(G_OBJECT(v), "close",
+ G_CALLBACK(closeview), c);
+ g_signal_connect(G_OBJECT(v), "create",
+ G_CALLBACK(createview), c);
+ g_signal_connect(G_OBJECT(v), "decide-policy",
+ G_CALLBACK(decidepolicy), c);
+ g_signal_connect(G_OBJECT(v), "insecure-content-detected",
+ G_CALLBACK(insecurecontent), c);
+ g_signal_connect(G_OBJECT(v), "load-failed-with-tls-errors",
+ G_CALLBACK(loadfailedtls), c);
+ g_signal_connect(G_OBJECT(v), "load-changed",
+ G_CALLBACK(loadchanged), c);
+ g_signal_connect(G_OBJECT(v), "mouse-target-changed",
+ G_CALLBACK(mousetargetchanged), c);
+ g_signal_connect(G_OBJECT(v), "permission-request",
+ G_CALLBACK(permissionrequested), c);
+ g_signal_connect(G_OBJECT(v), "ready-to-show",
+ G_CALLBACK(showview), c);
+ g_signal_connect(G_OBJECT(v), "web-process-terminated",
+ G_CALLBACK(webprocessterminated), c);
+
+ return v;
+}
+
+static gboolean
+readpipe(GIOChannel *s, GIOCondition ioc, gpointer unused)
+{
+ static char msg[MSGBUFSZ], msgsz;
+ GError *gerr = NULL;
+
+ if (g_io_channel_read_chars(s, msg, sizeof(msg), NULL, &gerr) !=
+ G_IO_STATUS_NORMAL) {
+ fprintf(stderr, "surf: error reading pipe: %s\n",
+ gerr->message);
+ g_error_free(gerr);
+ return TRUE;
+ }
+ if ((msgsz = msg[0]) < 3) {
+ fprintf(stderr, "surf: message too short: %d\n", msgsz);
+ return TRUE;
+ }
+
+ switch (msg[2]) {
+ case 'i':
+ close(pipein[1]);
+ close(pipeout[0]);
+ break;
+ }
+
+ return TRUE;
+}
+
+void
+initwebextensions(WebKitWebContext *wc, Client *c)
+{
+ GVariant *gv;
+
+ if (!pipeout[0] || !pipein[1])
+ return;
+
+ gv = g_variant_new("(ii)", pipeout[0], pipein[1]);
+
+ webkit_web_context_set_web_extensions_initialization_user_data(wc, gv);
+ webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR);
+}
+
+GtkWidget *
+createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c)
+{
+ Client *n;
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ /*
+ * popup windows of type “other” are almost always triggered
+ * by user gesture, so inverse the logic here
+ */
+/* instead of this, compare destination uri to mouse-over uri for validating window */
+ if (webkit_navigation_action_is_user_gesture(a))
+ return NULL;
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
+ n = newclient(c);
+ break;
+ default:
+ return NULL;
+ }
+
+ return GTK_WIDGET(n->view);
+}
+
+gboolean
+buttonreleased(GtkWidget *w, GdkEvent *e, Client *c)
+{
+ WebKitHitTestResultContext element;
+ int i;
+
+ element = webkit_hit_test_result_get_context(c->mousepos);
+
+ for (i = 0; i < LENGTH(buttons); ++i) {
+ if (element & buttons[i].target &&
+ e->button.button == buttons[i].button &&
+ CLEANMASK(e->button.state) == CLEANMASK(buttons[i].mask) &&
+ buttons[i].func) {
+ buttons[i].func(c, &buttons[i].arg, c->mousepos);
+ return buttons[i].stopevent;
+ }
+ }
+
+ return FALSE;
+}
+
+GdkFilterReturn
+processx(GdkXEvent *e, GdkEvent *event, gpointer d)
+{
+ Client *c = (Client *)d;
+ XPropertyEvent *ev;
+ Arg a;
+
+ if (((XEvent *)e)->type == PropertyNotify) {
+ ev = &((XEvent *)e)->xproperty;
+ if (ev->state == PropertyNewValue) {
+ if (ev->atom == atoms[AtomFind]) {
+ find(c, NULL);
+
+ return GDK_FILTER_REMOVE;
+ } else if (ev->atom == atoms[AtomGo]) {
+ a.v = getatom(c, AtomGo);
+ loaduri(c, &a);
+
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+gboolean
+winevent(GtkWidget *w, GdkEvent *e, Client *c)
+{
+ int i;
+
+ switch (e->type) {
+ case GDK_ENTER_NOTIFY:
+ c->overtitle = c->targeturi;
+ updatetitle(c);
+ break;
+ case GDK_KEY_PRESS:
+ if (!curconfig[KioskMode].val.i) {
+ for (i = 0; i < LENGTH(keys); ++i) {
+ if (gdk_keyval_to_lower(e->key.keyval) ==
+ keys[i].keyval &&
+ CLEANMASK(e->key.state) == keys[i].mod &&
+ keys[i].func) {
+ updatewinid(c);
+ keys[i].func(c, &(keys[i].arg));
+ return TRUE;
+ }
+ }
+ }
+ case GDK_LEAVE_NOTIFY:
+ c->overtitle = NULL;
+ updatetitle(c);
+ break;
+ case GDK_WINDOW_STATE:
+ if (e->window_state.changed_mask ==
+ GDK_WINDOW_STATE_FULLSCREEN)
+ c->fullscreen = e->window_state.new_window_state &
+ GDK_WINDOW_STATE_FULLSCREEN;
+ break;
+ default:
+ break;