偶尔一下的折腾

是社会道德的沦丧,还是人性的缺失,今天fortime带你探索消失的网页微信通知和不能横排的ibus-rime

消失的网页微信通知

由于firefox用着用着就占越来越多的内存,偶尔需要重启一下,在上面用网页微信的话,又要重新登录,当天的聊天记录又会没有了。所以,我最近用epiphany的app模式使用网页微信。用着用着,发现经常漏消息,通知栏的消息经常会消失。难道是因为webkit2gtk没有实现好libnotify,设置的expire时间不对?

没有过期时间的GnomeShell通知实现

通过grep,在WebKitWebView.cppwebkitWebViewShowNotification下找到了调用libnotifynotify_notification_show的代码。

static gboolean webkitWebViewShowNotification(WebKitWebView*, WebKitNotification* webNotification)
{
#if USE(LIBNOTIFY)
    if (!notify_is_initted())
        notify_init(g_get_prgname());

    NotifyNotification* notification = NOTIFY_NOTIFICATION(g_object_get_data(G_OBJECT(webNotification), gNotifyNotificationID))
;
    if (!notification) {
        notification = notify_notification_new(webkit_notification_get_title(webNotification),
            webkit_notification_get_body(webNotification), nullptr);

        notify_notification_add_action(notification, "default", _("Acknowledge"), NOTIFY_ACTION_CALLBACK(notifyNotificationClic
ked), webNotification, nullptr);
        notify_notification_set_timeout(notification, NOTIFY_EXPIRES_NEVER);

        g_signal_connect_object(notification, "closed", G_CALLBACK(notifyNotificationClosed), webNotification, static_cast<GCon
nectFlags>(0));
        g_signal_connect(webNotification, "closed", G_CALLBACK(webNotificationClosed), nullptr);
        g_object_set_data_full(G_OBJECT(webNotification), gNotifyNotificationID, notification, static_cast<GDestroyNotify>(g_ob
ject_unref));
    } else {
        notify_notification_update(notification, webkit_notification_get_title(webNotification),
            webkit_notification_get_body(webNotification), nullptr);
        notify_notification_set_timeout(notification, NOTIFY_EXPIRES_NEVER);
    }

    notify_notification_show(notification, nullptr);
    return TRUE;
#else
    UNUSED_PARAM(webNotification);
    return FALSE;
    #endif
}

哈哈,果然没有调用libnotifynotify_notification_set_timeout,果断设置为NOTIFY_EXPIRES_NEVER。打包,测试,GG😭。难道不能永久不过期?来一下60秒,打包,测试,GG😭。Google一下,GnomeShell没有实现libnotify的过期😫。难道firefox有什么黑科技,它是怎么实现通知的?

firefoxwebkit2gtk的通知实现比较


nsresult
nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
{
  if (!mBackend->IsActiveListener(mAlertName, this))
    return NS_OK;

  mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(),
                                          nullptr, nullptr);

  if (!mNotification)
    return NS_ERROR_OUT_OF_MEMORY;

  nsCOMPtr<nsIObserverService> obsServ =
      do_GetService("@mozilla.org/observer-service;1");
  if (obsServ)
    obsServ->AddObserver(this, "quit-application", true);

  if (aPixbuf)
    notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);

  NS_ADDREF(this);
  if (mAlertHasAction) {
    // What we put as the label doesn't matter here, if the action
    // string is "default" then that makes the entire bubble clickable
    // rather than creating a button.
    notify_notification_add_action(mNotification, "default", "Activate",
                                   notify_action_cb, this, nullptr);
  }

  // Fedora 10 calls NotifyNotification "closed" signal handlers with a
  // different signature, so a marshaller is used instead of a C callback to
  // get the user_data (this) in a parseable format.  |closure| is created
  // with a floating reference, which gets sunk by g_signal_connect_closure().
  GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
  g_closure_set_marshal(closure, notify_closed_marshal);
  mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE);
  GError* error = nullptr;
  if (!notify_notification_show(mNotification, &error)) {
    NS_WARNING(error->message);
    g_error_free(error);
    return NS_ERROR_FAILURE;
  }

  if (mAlertListener)
    mAlertListener->Observe(nullptr, "alertshow", mAlertCookie.get());

  return NS_OK;
}

哈,也是没有设置超时。而且还比webkit2gtk少了注册一些回调。如

g_signal_connect(webNotification, "closed", G_CALLBACK(webNotificationClosed), nullptr);

处理网页关闭通知的回调处理。

微信 - Do everything, but not good enough

既然firefox没有黑科技,那我只能继续测试了。每次测试还要找别人发微信消息给我,太麻烦了,自己写个button来触发通知。问题来了,我自己的测试网页发的通知没有消失。是时候看一下微信怎么发消息了。

function c(e, n) { 
    h.length >= M.total && h.shift().close();
    var o, c;
    return g && r() && angular.isString(e) && n && (angular.isString(n.icon) || angular.isObject(n.icon)) && i() === l && (o = t(e, n)), c = a(o), h.push(c), M.autoClose && o && !o.ieVerification && o.addEventListener && o.addEventListener("show", function() {
        var e = c;  
        setTimeout(function() {
            e.close()
        }, M.autoClose)
    }), o
}

心里万马奔腾,我怎么不早点看微信代码。估计是不同平台处理通知的逻辑不一样,微信网页版自己维护了通知列表,并5秒自动失效。由于firefox没有把网页的通知关闭signal转发给libnotify,所以系统的通知没有消失,而由于webkit2gtk实现的完整,所以通知5秒后消失了。这样magic的逻辑,微信应该加个开关啊。

解决方法

F12执行一下angular.element(document.querySelector('html')).injector().get('notificationFactory').config().autoClose=0把超时设置为0,就不会自动消失了。

TODO

  1. 如何自动执行F12那行呢?epiphany没有greasemonkey。
  2. webkit2gtk通知没有显示icon,怎么去到pixbuf来显示呢?
  3. 点击通知,如何bring forth窗口呢?

不能横排的ibus-rime

好像哪次更新ibus后,Google Docs上输入没有候选字。我就想改一下ibus的配置,看看能不能好转。不过,以前我想改一下ibus-rime的方向一直都没有成功过。这次我在default.custom.yaml和输入法的custom.yaml上加style/horizontal: true,嗯,还是没有成功。是不是由于true不对呢?我把各种true都试了,还是不行。
又要施展源代码大法

// rime_settings.c
#include "rime_config.h"
#include <string.h>
#include <ibus.h>
#include <rime_api.h>
#include "rime_settings.h"

static struct ColorSchemeDefinition preset_color_schemes[] = {
  { "aqua", 0xffffff, 0x0a3dfa },
  { "azure", 0xffffff, 0x0a3dea },
  { "ink", 0xffffff, 0x000000 },
  { "luna", 0x000000, 0xffff7f },
  { NULL, 0, 0 }
};

static struct IBusRimeSettings ibus_rime_settings_default = {
  FALSE,
  IBUS_ORIENTATION_SYSTEM,
  &preset_color_schemes[0],
};

struct IBusRimeSettings g_ibus_rime_settings;

static void
select_color_scheme(struct IBusRimeSettings* settings,
		    const char* color_scheme_id)
{
  struct ColorSchemeDefinition* c;
  for (c = preset_color_schemes; c->color_scheme_id; ++c) {
    if (!strcmp(c->color_scheme_id, color_scheme_id)) {
      settings->color_scheme = c;
      g_debug("selected color scheme: %s", color_scheme_id);
      return;
    }
  }
  // fallback to default
  settings->color_scheme = &preset_color_schemes[0];
}

void
ibus_rime_load_settings()
{
  g_ibus_rime_settings = ibus_rime_settings_default;

  RimeConfig config = {0};
  if (!RimeConfigOpen("ibus_rime", &config)) {
    g_error("error loading settings for ibus_rime");
    return;
  }

  Bool inline_preedit = False;
  if (RimeConfigGetBool(&config, "style/inline_preedit", &inline_preedit)) {
    g_ibus_rime_settings.embed_preedit_text = !!inline_preedit;
  }

  Bool horizontal = False;
  if (RimeConfigGetBool(&config, "style/horizontal", &horizontal)) {
    g_ibus_rime_settings.lookup_table_orientation =
      horizontal ? IBUS_ORIENTATION_HORIZONTAL : IBUS_ORIENTATION_VERTICAL;
  }

  const char* color_scheme =
    RimeConfigGetCString(&config, "style/color_scheme");
  if (color_scheme) {
    select_color_scheme(&g_ibus_rime_settings, color_scheme);
  }

  RimeConfigClose(&config);
}

RimeConfigOpen("ibus_rime", &config)不就是读取了rime的配置吗,那是为什么没有生效呢?然后有去看看librime,究竟是怎么加载default.yaml的。看着看着,突然想到"ibus_rime"是不是不是指default.yaml呢?说干就干,创建一个*~/.config/ibus/rime/build/ibus_rime.yaml*,内容为:

style:
    horizontal: True
    inline_preedit: True
    color_scheme: azure

哇,还真生效了,inline_preedit也出来了,颜色也有。在测试的过程中,发现重启系统后,Google Docs是有显示侯选字的,但执行多几次ibus-daemon -drx后,又没有了。所以,这可能是ibus某些很深的逻辑没有处理好。

总结

以上说这么多,其实,就是为了记录:

  1. angular.element(document.querySelector('html')).injector().get('notificationFactory').config().autoClose=0
  2. ibus-rime的配置文件是ibus_rime.yaml,而且要放build下。