diff --git a/src/meson.build b/src/meson.build
index 8bca3dd8e8cd1b19146cf3b6669c55782726835d..2757c4118dbe6789e7fa70265f5d5ce08b67a0b2 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -22,6 +22,7 @@ phosh_resources = gnome.compile_resources(
 
 wl_protos = [
   '/'.join([wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml']),
+  '../protocol/gamma-control.xml',
   '../protocol/idle.xml',
   '../protocol/phosh-private.xml',
   '../protocol/wlr-input-inhibitor-unstable-v1.xml',
diff --git a/src/monitor-manager.c b/src/monitor-manager.c
index f47afb7cee4f0915376bb8f23dd40b452c1ee82d..2b54cfdb2da8250220e0c905320e83cb3806db9e 100644
--- a/src/monitor-manager.c
+++ b/src/monitor-manager.c
@@ -10,8 +10,13 @@
 
 #include "monitor-manager.h"
 #include "monitor/monitor.h"
+
+#include "gamma-control-client-protocol.h"
+#include "phosh.h"
+
 #include <gdk/gdkwayland.h>
 
+
 static void phosh_monitor_manager_display_config_init (
   PhoshDisplayDbusOrgGnomeMutterDisplayConfigIface *iface);
 
@@ -135,6 +140,7 @@ phosh_monitor_manager_handle_get_resources (
   return TRUE;
 }
 
+
 static gboolean
 phosh_monitor_manager_handle_change_backlight (
   PhoshDisplayDbusOrgGnomeMutterDisplayConfig *skeleton,
@@ -148,6 +154,52 @@ phosh_monitor_manager_handle_change_backlight (
 }
 
 
+struct get_wl_gamma_callback_data {
+  PhoshDisplayDbusOrgGnomeMutterDisplayConfig *skeleton;
+  GDBusMethodInvocation *invocation;
+};
+
+
+static void handle_wl_gamma_size(void *data, struct gamma_control *gamma_control,
+                                 uint32_t size) {
+  struct get_wl_gamma_callback_data *gamma_callback_data = data;
+  GBytes *red_bytes, *green_bytes, *blue_bytes;
+  GVariant *red_v, *green_v, *blue_v;
+  /* All known clients using libgnome-desktop's
+     gnome_rr_crtc_get_gamma only do so to get the size of the gamma
+     table. So don't bother getting the real table since this is not
+     supported by wlroots: https://github.com/swaywm/wlroots/pull/1059.
+     Return an empty table instead.
+  */
+  size *= sizeof(unsigned short);
+  red_bytes = g_bytes_new_take (g_malloc0 (size), size);
+  green_bytes = g_bytes_new_take (g_malloc0 (size), size);
+  blue_bytes = g_bytes_new_take (g_malloc0 (size), size);
+
+  red_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), red_bytes, TRUE);
+  green_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), green_bytes, TRUE);
+  blue_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), blue_bytes, TRUE);
+
+  phosh_display_dbus_org_gnome_mutter_display_config_complete_get_crtc_gamma (
+    gamma_callback_data->skeleton,
+    gamma_callback_data->invocation,
+    red_v, green_v, blue_v);
+
+  g_bytes_unref (red_bytes);
+  g_bytes_unref (green_bytes);
+  g_bytes_unref (blue_bytes);
+
+  g_free (gamma_callback_data);
+  gamma_control_destroy (gamma_control);
+}
+
+
+static const struct
+gamma_control_listener gamma_control_listener = {
+	.gamma_size = handle_wl_gamma_size,
+};
+
+
 static gboolean
 phosh_monitor_manager_handle_get_crtc_gamma (
   PhoshDisplayDbusOrgGnomeMutterDisplayConfig *skeleton,
@@ -155,8 +207,47 @@ phosh_monitor_manager_handle_get_crtc_gamma (
   guint                  serial,
   guint                  crtc_id)
 {
-  g_debug ("Unimplemented DBus call %s\n", __func__);
-  return FALSE;
+  PhoshMonitorManager *self = PHOSH_MONITOR_MANAGER (skeleton);
+  PhoshMonitor *monitor;
+  struct gamma_control *gamma_control;
+  struct gamma_control_manager *gamma_control_manager;
+  struct get_wl_gamma_callback_data *data;;
+
+  g_debug ("DBus call %s for crtc %d, serial %d\n", __func__, crtc_id, serial);
+
+  if (serial != self->serial) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                           G_DBUS_ERROR_ACCESS_DENIED,
+                                           "The requested configuration is based on stale information");
+    return TRUE;
+  }
+
+  if (crtc_id >= self->monitors->len) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                           G_DBUS_ERROR_INVALID_ARGS,
+                                           "Invalid crtc id %d", crtc_id);
+    return TRUE;
+  }
+
+  gamma_control_manager = phosh_shell_get_wl_gamma_control_manager ();
+  if (gamma_control_manager == NULL) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                           G_DBUS_ERROR_NOT_SUPPORTED,
+                                           "gamma control not supported");
+    return TRUE;
+  }
+
+  data = g_new0 (struct get_wl_gamma_callback_data, 1);
+  data->skeleton = skeleton;
+  data->invocation = invocation;
+
+  monitor = g_ptr_array_index (self->monitors, crtc_id);
+  gamma_control = gamma_control_manager_get_gamma_control (
+    gamma_control_manager,
+    monitor->wl_output);
+  gamma_control_add_listener (gamma_control, &gamma_control_listener, data);
+
+  return TRUE;
 }
 
 
@@ -170,8 +261,87 @@ phosh_monitor_manager_handle_set_crtc_gamma (
   GVariant              *green_v,
   GVariant              *blue_v)
 {
-  g_debug ("Unimplemented DBus call %s\n", __func__);
-  return FALSE;
+  PhoshMonitorManager *self = PHOSH_MONITOR_MANAGER (skeleton);
+  PhoshMonitor *monitor;
+  unsigned short *red, *green, *blue;
+  GBytes *red_bytes, *green_bytes, *blue_bytes;
+  gsize size, dummy;
+  struct gamma_control_manager *gamma_control_manager;
+  struct gamma_control *gamma_control;
+  struct wl_array wl_red, wl_green, wl_blue;
+
+  g_debug ("DBus call %s for crtc %d, serial %d\n", __func__, crtc_id, serial);
+  if (serial != self->serial) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                           G_DBUS_ERROR_ACCESS_DENIED,
+                                           "The requested configuration is based on stale information");
+      return TRUE;
+  }
+
+  if (crtc_id >= self->monitors->len) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                           G_DBUS_ERROR_INVALID_ARGS,
+                                           "Invalid crtc id");
+    return TRUE;
+  }
+
+  gamma_control_manager = phosh_shell_get_wl_gamma_control_manager ();
+  if (!gamma_control_manager) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                           G_DBUS_ERROR_NOT_SUPPORTED,
+                                           "gamma control not supported");
+    return TRUE;
+  }
+
+  monitor = g_ptr_array_index (self->monitors, crtc_id);
+
+  red_bytes = g_variant_get_data_as_bytes (red_v);
+  green_bytes = g_variant_get_data_as_bytes (green_v);
+  blue_bytes = g_variant_get_data_as_bytes (blue_v);
+
+  size = g_bytes_get_size (red_bytes);
+  if (size != g_bytes_get_size (blue_bytes) || size != g_bytes_get_size (green_bytes)) {
+        g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                           G_DBUS_ERROR_NOT_SUPPORTED,
+                                           "gamma for each color must have same size");
+    goto err;
+  }
+
+  red = (unsigned short*) g_bytes_get_data (red_bytes, &dummy);
+  green = (unsigned short*) g_bytes_get_data (green_bytes, &dummy);
+  blue = (unsigned short*) g_bytes_get_data (blue_bytes, &dummy);
+
+  wl_array_init (&wl_red);
+  wl_array_init (&wl_green);
+  wl_array_init (&wl_blue);
+
+  wl_array_add (&wl_red, size);
+  wl_array_add (&wl_green, size);
+  wl_array_add (&wl_blue, size);
+
+  memcpy(wl_red.data, red, size);
+  memcpy(wl_green.data, green, size);
+  memcpy(wl_blue.data, blue, size);
+
+  gamma_control = gamma_control_manager_get_gamma_control (
+    gamma_control_manager,
+    monitor->wl_output);
+  gamma_control_set_gamma(gamma_control, &wl_red, &wl_green, &wl_blue);
+  gamma_control_destroy (gamma_control);
+
+  phosh_display_dbus_org_gnome_mutter_display_config_complete_set_crtc_gamma (
+      skeleton,
+      invocation);
+
+  wl_array_release (&wl_red);
+  wl_array_release (&wl_green);
+  wl_array_release (&wl_blue);
+
+ err:
+  g_bytes_unref (red_bytes);
+  g_bytes_unref (green_bytes);
+  g_bytes_unref (blue_bytes);
+  return TRUE;
 }
 
 #define MODE_FORMAT "(siiddada{sv})"
diff --git a/src/monitor-manager.h b/src/monitor-manager.h
index f882d4ade0906930f9fdc7c40851e47eb66384b0..1f8e3c5538229bbc64912170636d46568e58f07c 100644
--- a/src/monitor-manager.h
+++ b/src/monitor-manager.h
@@ -13,9 +13,11 @@
 G_DECLARE_FINAL_TYPE (PhoshMonitorManager, phosh_monitor_manager, PHOSH, MONITOR_MANAGER,
                       PhoshDisplayDbusOrgGnomeMutterDisplayConfigSkeleton)
 
-PhoshMonitorManager * phosh_monitor_manager_new         (void);
-void                  phosh_monitor_manager_add_monitor (PhoshMonitorManager *self,
-                                                         PhoshMonitor        *monitor);
-PhoshMonitor        * phosh_monitor_manager_get_monitor (PhoshMonitorManager *self,
-                                                         guint monitor);
-guint                 phosh_monitor_manager_get_num_monitors (PhoshMonitorManager *self);
+PhoshMonitorManager * phosh_monitor_manager_new                       (void);
+void                  phosh_monitor_manager_add_monitor               (PhoshMonitorManager *self,
+                                                                       PhoshMonitor        *monitor);
+PhoshMonitor        * phosh_monitor_manager_get_monitor               (PhoshMonitorManager *self,
+                                                                       guint                monitor);
+guint                 phosh_monitor_manager_get_num_monitors          (PhoshMonitorManager *self);
+void                  phosh_monitor_manager_set_gamma_control_manager (PhoshMonitorManager *self,
+                                                                       gpointer            *gamma);
diff --git a/src/phosh.c b/src/phosh.c
index dda382f003dbaa7def2695ca5eb0b58f4423a486..3aa853f3133fc363a30e62873c5197fc3e0902b7 100644
--- a/src/phosh.c
+++ b/src/phosh.c
@@ -26,7 +26,7 @@
 #include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
 #include "wlr-layer-shell-unstable-v1-client-protocol.h"
 #include "xdg-shell-client-protocol.h"
-
+#include "gamma-control-client-protocol.h"
 #include "phosh.h"
 
 #include "monitor/monitor.h"  /* FIXME: move upwards? */
@@ -70,6 +70,7 @@ typedef struct
   struct org_kde_kwin_idle *idle_manager;
   struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager;
   struct zwlr_input_inhibitor_v1 *input_inhibitor;
+  struct gamma_control_manager *gamma_control_manager;
   struct wl_seat *wl_seat;
   struct xdg_wm_base *xdg_wm_base;
 
@@ -653,24 +654,36 @@ registry_handle_global (void *data,
   PhoshShellPrivate *priv = phosh_shell_get_instance_private (self);
   struct wl_output *output;
 
-
   if (!strcmp (interface, "phosh_private")) {
-      priv->mshell = wl_registry_bind (registry, name,
-          &phosh_private_interface, 1);
+      priv->mshell = wl_registry_bind (
+        registry,
+        name,
+        &phosh_private_interface,
+        1);
   } else  if (!strcmp (interface, zwlr_layer_shell_v1_interface.name)) {
-      priv->layer_shell = wl_registry_bind (registry, name,
-          &zwlr_layer_shell_v1_interface, 1);
+      priv->layer_shell = wl_registry_bind (
+        registry,
+        name,
+        &zwlr_layer_shell_v1_interface,
+        1);
   } else if (!strcmp (interface, "wl_output")) {
-    output = wl_registry_bind (registry, name,
-          &wl_output_interface, 1);
-    phosh_monitor_manager_add_monitor (priv->monitor_manager,
-                                       phosh_monitor_new_from_wl_output(output));
+    output = wl_registry_bind (
+      registry,
+      name,
+      &wl_output_interface, 1);
+    phosh_monitor_manager_add_monitor (
+      priv->monitor_manager,
+      phosh_monitor_new_from_wl_output(output));
   } else if (!strcmp (interface, "org_kde_kwin_idle")) {
-    priv->idle_manager = wl_registry_bind (registry,
-                                           name,
-                                           &org_kde_kwin_idle_interface, 1);
+    priv->idle_manager = wl_registry_bind (
+      registry,
+      name,
+      &org_kde_kwin_idle_interface,
+      1);
   } else if (!strcmp(interface, "wl_seat")) {
-    priv->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
+    priv->wl_seat = wl_registry_bind(
+      registry, name, &wl_seat_interface,
+      1);
   } else if (!strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name)) {
     priv->input_inhibit_manager = wl_registry_bind(
       registry,
@@ -678,7 +691,17 @@ registry_handle_global (void *data,
       &zwlr_input_inhibit_manager_v1_interface,
       1);
   } else if (!strcmp(interface, xdg_wm_base_interface.name)) {
-    priv->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
+    priv->xdg_wm_base = wl_registry_bind(
+      registry,
+      name,
+      &xdg_wm_base_interface,
+      1);
+  } else if (!strcmp(interface, gamma_control_manager_interface.name)) {
+    priv->gamma_control_manager = wl_registry_bind(
+      registry,
+      name,
+      &gamma_control_manager_interface,
+      1);
   }
 }
 
@@ -768,7 +791,7 @@ phosh_shell_constructed (GObject *object)
 {
   PhoshShell *self = PHOSH_SHELL (object);
   PhoshShellPrivate *priv = phosh_shell_get_instance_private (self);
-  guint num_mon;;
+  guint num_mon;
 
   G_OBJECT_CLASS (phosh_shell_parent_class)->constructed (object);
 
@@ -788,26 +811,34 @@ phosh_shell_constructed (GObject *object)
   /* Wait until we have been notified about the compositor,
    * shell, and shell helper objects */
   num_mon = phosh_monitor_manager_get_num_monitors (priv->monitor_manager);
-  if (!num_mon || !priv->layer_shell || !priv->idle_manager ||
-      !priv->input_inhibit_manager || !priv->mshell || !priv->xdg_wm_base)
+  if (!num_mon ||
+      !priv->layer_shell ||
+      !priv->idle_manager ||
+      !priv->input_inhibit_manager ||
+      !priv->mshell ||
+      !priv->xdg_wm_base ||
+      !priv->gamma_control_manager)
     wl_display_roundtrip (priv->display);
   num_mon = phosh_monitor_manager_get_num_monitors (priv->monitor_manager);
-  if (!num_mon || !priv->layer_shell || !priv->idle_manager ||
-      !priv->input_inhibit_manager || !priv->xdg_wm_base) {
+  if (!num_mon ||
+      !priv->layer_shell ||
+      !priv->idle_manager ||
+      !priv->input_inhibit_manager ||
+      !priv->xdg_wm_base ||
+      !priv->gamma_control_manager) {
     g_error ("Could not find needed globals\n"
              "outputs: %d, layer_shell: %p, seat: %p, "
-             "inhibit: %p, xdg_wm: %p\n",
+             "inhibit: %p, xdg_wm: %p, gamma %p\n",
              num_mon, priv->layer_shell, priv->idle_manager,
-             priv->input_inhibit_manager, priv->xdg_wm_base);
+             priv->input_inhibit_manager, priv->xdg_wm_base,
+             priv->gamma_control_manager);
   }
   if (!priv->mshell) {
     g_info ("Could not find phosh global, disabling some features\n");
   }
 
-
   gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (),
                                     "/sm/puri/phosh/icons");
-
   env_setup ();
   css_setup (self);
   panel_create (self);
@@ -884,6 +915,14 @@ phosh_shell_get_wl_layer_shell ()
 }
 
 
+gpointer
+phosh_shell_get_wl_gamma_control_manager ()
+{
+  PhoshShellPrivate *priv = phosh_shell_get_instance_private (_phosh);
+  return priv->gamma_control_manager;
+}
+
+
 PhoshMonitor *
 phosh_shell_get_primary_monitor ()
 {
diff --git a/src/phosh.h b/src/phosh.h
index 74bfbe55ffdd62e31bd0c1d35a93831c07b01f6e..e17adb368b4bf4cad2dba9e995553a6073faaff8 100644
--- a/src/phosh.h
+++ b/src/phosh.h
@@ -31,4 +31,5 @@ PhoshMonitor        *phosh_shell_get_primary_monitor ();
 
 /* Phosh keeps track of the wayland globals */
 gpointer             phosh_shell_get_wl_layer_shell  ();
+gpointer             phosh_shell_get_wl_gamma_control_manager ();
 #endif /* PHOSH_H */