Index: setup.py
===================================================================
--- setup.py	(revision 7630)
+++ setup.py	(working copy)
@@ -124,7 +124,7 @@
 enc_proxy_ENABLED       = True
 enc_x264_ENABLED        = True          #too important to detect
 enc_x265_ENABLED        = pkg_config_ok("--exists", "x265")
-webp_ENABLED            = pkg_config_ok("--atleast-version=0.3", "libwebp", fallback=WIN32)
+webp_ENABLED            = pkg_config_ok("--atleast-version=0.3", "libwebp", fallback=WIN32 and not PYTHON3)
 x264_static_ENABLED     = False
 x265_static_ENABLED     = False
 webp_static_ENABLED     = False
@@ -555,7 +555,7 @@
                     valid_option = option
                     break
             if not valid_option:
-                sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
+                raise Exception("ERROR: cannot find a valid pkg-config package for %s" % (options,))
             package_names.append(valid_option)
         if verbose_ENABLED and list(pkgs_options)!=list(package_names):
             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
@@ -565,7 +565,7 @@
         cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
         r, out, err = get_status_output(cmd)
         if r!=0:
-            sys.exit("ERROR: call to pkg-config ('%s') failed (err=%s)" % (" ".join(cmd), err))
+            raise Exception("ERROR: call to pkg-config ('%s') failed (err=%s)" % (" ".join(cmd), err))
         if sys.version>='3':
             out = out.decode('utf-8')
         for token in out.split():
@@ -927,6 +927,16 @@
     pango_include_dir       = os.path.join(gtk2_base_include_dir, "pango-1.0")
     gdkconfig_include_dir   = os.path.join(gtk2runtime_path, "lib", "gtk-2.0", "include")
     glibconfig_include_dir  = os.path.join(gtk2runtime_path, "lib", "glib-2.0", "include")
+
+    #FIXME: ugly workarounds for building some win32 workarounds:
+    # * pycairo set_image_surface_data 
+    # * win32_get_hwnd
+    #the win32 py-gi installers don't have development headers
+    #so we hardcode them here instead...
+    #(until someone fixes the win32 builds properly)
+    GTK3_HEADERS = "C:\\GTK3\\include\\"
+    MINGW_HEADERS = "C:\\MinGW\\include\\"
+
     #END OF HARDCODED SECTION
     ###########################################################
 
@@ -1303,8 +1313,21 @@
                     cairo_include_dir, pango_include_dir)
             add_to_keywords(kw, 'include_dirs', *dirs)
             checkdirs(*dirs)
+        elif "pycairo" in pkgs_options:
+            assert PYTHON3
+            kw = exec_pkgconfig(**ekw)
+            add_to_keywords(kw, 'include_dirs', GTK3_HEADERS+"pycairo-1.10.0")
+        elif "gdk-3.0" in pkgs_options:
+            assert PYTHON3
+            #note: we only include a subset,
+            #just enough to build the extension we care about..
+            kw = exec_pkgconfig(**ekw)
+            add_to_keywords(kw, 'include_dirs', MINGW_HEADERS)
+            for x in ("gtk-3.0", "glib-2.0", "pango-1.0", "cairo", "gdk-pixbuf-2.0", ""):
+                add_to_keywords(kw, 'include_dirs', GTK3_HEADERS+x)
+            add_to_keywords(kw, 'libraries', "gdk-3", "gobject-2.0")
         else:
-            sys.exit("ERROR: unknown package config: %s" % str(pkgs_options))
+            raise Exception("ERROR: unknown package config: %s" % str(pkgs_options))
         if debug_ENABLED:
             #Od will override whatever may be specified elsewhere
             #and allows us to use the debug switches,
@@ -1317,22 +1340,15 @@
         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
         return kw
 
-    if not has_pkg_config:
-        #use the hardcoded version above:
-        pkgconfig = VC_pkgconfig
-    else:
-        #FIXME: ugly workaround for building the ugly pycairo workaround on win32:
-        #the win32 py-gi installers don't have development headers for pycairo
-        #so we hardcode them here instead...
-        #(until someone fixes the win32 builds properly)
-        def pkgconfig(*pkgs_options, **ekw):
-            if "pycairo" in pkgs_options:
-                kw = exec_pkgconfig("cairo", **ekw)
-                add_to_keywords(kw, 'include_dirs', "C:\\pycairo-1.10.0")
-                return kw
+    def pkgconfig_fallback(*pkgs_options, **ekw):
+        try:
             return exec_pkgconfig(*pkgs_options, **ekw)
+        except Exception as e:
+            print("pkgconfig failed: %s" % e)
+            print("trying fallback..")
+            return VC_pkgconfig(*pkgs_options, **ekw)
+    pkgconfig = pkgconfig_fallback
 
-
     remove_packages(*external_excludes)
     remove_packages(#not used on win32:
                     "mmap",
@@ -1596,6 +1612,12 @@
                 ["xpra/client/gtk3/cairo_workaround.pyx", buffers_c],
                 **pkgconfig("pycairo")
                 ))
+    if WIN32 or True:
+        #win32 hwnd workaround:
+        cython_add(Extension("xpra.client.gtk3.win32_get_hwnd",
+                    ["xpra/client/gtk3/win32_get_hwnd.pyx"],
+                    **pkgconfig("gdk-3.0")
+                    ))
 
 add_packages("xpra.codecs.argb")
 cython_add(Extension("xpra.codecs.argb.argb",
Index: xpra/client/gtk3/test_hwnd.py
===================================================================
--- xpra/client/gtk3/test_hwnd.py	(revision 0)
+++ xpra/client/gtk3/test_hwnd.py	(working copy)
@@ -0,0 +1,16 @@
+# coding=utf8
+# This file is part of Xpra.
+# Copyright (C) 2014 Antoine Martin <antoine@devloop.org.uk>
+# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
+# later version. See the file COPYING for details.
+
+from xpra.client.gtk3.win32_get_hwnd import get_window_size, get_window_hwnd
+
+from gi.repository import Gtk   #@UnresolvedImport
+
+w = Gtk.Window()
+w.set_size_request(300, 200)
+w.realize()
+gdkw = w.get_window()
+print("get_window_size(%s)=%s" % (gdkw, get_window_size(gdkw)))
+print("get_window_hwnd(%s)=%s" % (gdkw, get_window_hwnd(gdkw)))
Index: xpra/client/gtk3/win32_get_hwnd.pyx
===================================================================
--- xpra/client/gtk3/win32_get_hwnd.pyx	(revision 0)
+++ xpra/client/gtk3/win32_get_hwnd.pyx	(working copy)
@@ -0,0 +1,48 @@
+# This file is part of Xpra.
+# Copyright (C) 2014 Antoine Martin <antoine@devloop.org.uk>
+# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
+# later version. See the file COPYING for details.
+
+# What is this workaround you ask?
+# Well, the new shiny GTK3 gobject introspection bindings
+# do not have what we need, yet again.
+
+from gi.repository import Gtk, GdkX11
+
+
+cdef extern from "Python.h":
+    ctypedef struct PyObject:
+        pass
+
+cdef extern from "sys/types.h":
+    ctypedef int uid_t
+
+cdef extern from "gdk/gdk.h":
+    ctypedef struct GdkWindow:
+        pass
+    ctypedef int HGDIOBJ
+    HGDIOBJ gdk_win32_window_get_handle(GdkWindow *window)
+    int gdk_window_get_width(GdkWindow *window)
+    int gdk_window_get_height(GdkWindow *window)
+
+cdef extern from "pygobject-3.0/pygobject.h":
+    ctypedef struct PyGObject:
+        #GObject *obj
+        void *obj
+
+def get_window_hwnd(object window):
+    #convert pixel_data to a C buffer:
+    print("get_window_hwnd(%s) type=%s" % (window, type(window)))
+    if not isinstance(window, GdkX11.X11Window):
+        raise TypeError("object %r is not a %r" % (window, GdkX11.X11Window))
+    cdef PyGObject *pygobj = <PyGObject *> window
+    cdef GdkWindow *w = <GdkWindow *> pygobj.obj
+    gdk_win32_window_get_handle(w)
+
+def get_window_size(object window):
+    #convert pixel_data to a C buffer:
+    if not isinstance(window, GdkX11.X11Window):
+        raise TypeError("object %r is not a %r" % (window, GdkX11.X11Window))
+    cdef PyGObject *pygobj = <PyGObject *> window   #
+    cdef GdkWindow *w = <GdkWindow *> pygobj.obj    #
+    return gdk_window_get_width(w), gdk_window_get_height(w)
