diff src/aosd/ghosd-main.c @ 569:d401f87f89f7 trunk

[svn] - added Audacious OSD, yet-another-written-from-scratch plugin to display OSD, based on Ghosd library; currently untied from configure, to compile it you have to run make in its directory; will be added to configure after some testing
author giacomo
date Mon, 29 Jan 2007 06:40:04 -0800
parents
children f574c2c52beb
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd-main.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,171 @@
+/* ghosd -- OSD with fake transparency, cairo, and pango.
+ * Copyright (C) 2006 Evan Martin <martine@danga.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ghosd.h"
+#include "ghosd-internal.h"
+
+static void
+ghosd_main_iteration(Ghosd *ghosd) {
+  XEvent ev, pev;
+  XNextEvent(ghosd->dpy, &ev);
+  
+  /* smash multiple configure/exposes into one. */
+  if (ev.type == ConfigureNotify) {
+    while (XPending(ghosd->dpy)) {
+      XPeekEvent(ghosd->dpy, &pev);
+      if (pev.type != ConfigureNotify && pev.type != Expose)
+        break;
+      XNextEvent(ghosd->dpy, &ev);
+    }
+  }
+
+  switch (ev.type) {
+  case Expose:
+    break;
+  case ConfigureNotify:
+    if (ghosd->width > 0) {
+      /* XXX if the window manager disagrees with our positioning here,
+       * we loop. */
+      if (ghosd->x != ev.xconfigure.x ||
+          ghosd->y != ev.xconfigure.y) {
+        /*width = ev.xconfigure.width; 
+        height = ev.xconfigure.height;*/
+        XMoveResizeWindow(ghosd->dpy, ghosd->win,
+                          ghosd->x, ghosd->y, ghosd->width, ghosd->height);
+      }
+    }
+    break;
+  }
+}
+
+void
+ghosd_main_iterations(Ghosd *ghosd) {
+  while (XPending(ghosd->dpy))
+    ghosd_main_iteration(ghosd);
+}
+
+void
+ghosd_main_until(Ghosd *ghosd, struct timeval *until) {
+  struct timeval tv_now;
+
+  ghosd_main_iterations(ghosd);
+
+  for (;;) {
+    gettimeofday(&tv_now, NULL);
+    int dt = (until->tv_sec  - tv_now.tv_sec )*1000 +
+             (until->tv_usec - tv_now.tv_usec)/1000;
+    if (dt <= 0) break;
+
+    struct pollfd pollfd = { ghosd_get_socket(ghosd), POLLIN, 0 };
+    int ret = poll(&pollfd, 1, dt);
+    if (ret < 0) {
+      perror("poll");
+      exit(1);
+    } else if (ret > 0) {
+      ghosd_main_iterations(ghosd);
+    } else {
+      /* timer expired. */
+      break;
+    }
+  }
+}
+
+typedef struct {
+  cairo_surface_t* surface;
+  float alpha;
+  RenderCallback user_render;
+} GhosdFlashData;
+
+static void
+flash_render(Ghosd *ghosd, cairo_t *cr, void* data) {
+  GhosdFlashData *flash = data;
+
+  /* the first time we render, let the client render into their own surface. */
+  if (flash->surface == NULL) {
+    cairo_t *rendered_cr;
+    flash->surface = cairo_surface_create_similar(cairo_get_target(cr),
+                                                  CAIRO_CONTENT_COLOR_ALPHA,
+                                                  ghosd->width, ghosd->height);
+    rendered_cr = cairo_create(flash->surface);
+    flash->user_render.func(ghosd, rendered_cr, flash->user_render.data);
+    cairo_destroy(rendered_cr);
+  }
+
+  /* now that we have a rendered surface, all we normally do is copy that to
+   * the screen. */
+  cairo_set_source_surface(cr, flash->surface, 0, 0);
+  cairo_paint_with_alpha(cr, flash->alpha);
+}
+
+/* we don't need to free the flashdata object, because we stack-allocate that.
+ * but we do need to let the old user data free itself... */
+static void
+flash_destroy(void *data) {
+  GhosdFlashData *flash = data;
+  if (flash->user_render.data_destroy)
+    flash->user_render.data_destroy(flash->user_render.data);
+}
+
+void
+ghosd_flash(Ghosd *ghosd, int fade_ms, int total_display_ms) {
+  GhosdFlashData flash = {0};
+  memcpy(&flash.user_render, &ghosd->render, sizeof(RenderCallback));
+  ghosd_set_render(ghosd, flash_render, &flash, flash_destroy);
+
+  ghosd_show(ghosd);
+
+  const int STEP_MS = 50;
+  const float dalpha = 1.0 / (fade_ms / (float)STEP_MS);
+  struct timeval tv_nextupdate;
+
+  /* fade in. */
+  for (flash.alpha = 0; flash.alpha < 1.0; flash.alpha += dalpha) {
+    if (flash.alpha > 1.0) flash.alpha = 1.0;
+    ghosd_render(ghosd);
+
+    gettimeofday(&tv_nextupdate, NULL);
+    tv_nextupdate.tv_usec += STEP_MS*1000;
+    ghosd_main_until(ghosd, &tv_nextupdate);
+  }
+
+  /* full display. */
+  flash.alpha = 1.0;
+  ghosd_render(ghosd);
+
+  gettimeofday(&tv_nextupdate, NULL);
+  tv_nextupdate.tv_usec += (total_display_ms - (2*fade_ms))*1000;
+  ghosd_main_until(ghosd, &tv_nextupdate);
+
+  /* fade out. */
+  for (flash.alpha = 1.0; flash.alpha > 0.0; flash.alpha -= dalpha) {
+    ghosd_render(ghosd);
+
+    gettimeofday(&tv_nextupdate, NULL);
+    tv_nextupdate.tv_usec += STEP_MS*1000;
+    ghosd_main_until(ghosd, &tv_nextupdate);
+  }
+
+  flash.alpha = 0;
+  ghosd_render(ghosd);
+
+  /* display for another half-second,
+   * because otherwise the fade out attracts your eye
+   * and then you'll see a flash while it repaints where the ghosd was.
+   */
+  gettimeofday(&tv_nextupdate, NULL);
+  tv_nextupdate.tv_usec += 500*1000;
+  ghosd_main_until(ghosd, &tv_nextupdate);
+}
+
+/* vim: set ts=2 sw=2 et cino=(0 : */