From 89f245e8db86b8f9bd14bd184aed4c7ae571dcde Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 22 May 2014 02:40:42 +0200 Subject: misc + fake-w32-gdi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/fake-w32-gdi.c | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/fake-w32-gdi.h | 80 ++++++++++++++ src/gamma-dummy.h | 1 - src/gamma-linux-drm.h | 1 - src/gamma-quartz-cg.c | 4 +- src/gamma-quartz-cg.h | 1 - src/gamma-w32-gdi.c | 36 +++---- src/gamma-w32-gdi.h | 1 - src/gamma-x-randr.h | 1 - src/gamma-x-vidmode.h | 1 - src/libgamma-method.h | 20 ++-- 11 files changed, 392 insertions(+), 36 deletions(-) create mode 100644 src/fake-w32-gdi.c create mode 100644 src/fake-w32-gdi.h (limited to 'src') diff --git a/src/fake-w32-gdi.c b/src/fake-w32-gdi.c new file mode 100644 index 0000000..bfd667a --- /dev/null +++ b/src/fake-w32-gdi.c @@ -0,0 +1,282 @@ +/** + * libgamma — Display server abstraction layer for gamma ramp adjustments + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library. If not, see . + */ +#ifndef HAVE_GAMMA_METHOD_W32_GDI +# error Compiling fake-w32-gdi.c without FAKE_GAMMA_METHOD_W32_GDI +#endif + + +#include "fake-w32-gdi.h" + +#include +#include +#include + + +#ifndef HAVE_GAMMA_METHOD_X_RANDR + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */ +HDC GetDC(HWND hWnd) +{ + (void) hWnd; + return (HDC*)16; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */ +int ReleaseDC(HWND hWnd, HDC hDC) +{ + (void) hWnd; + (void) hDC; + return 1; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */ +int GetDeviceCaps(HDC hDC, int nIndex) +{ + (void) hDC; + return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */ +BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + (void) hDC; + (void) lpRamp; + return TRUE; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */ +BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + (void) hDC; + (void) lpRamp; + return TRUE; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */ +HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void* lpszOutput, void* lpInitData) +{ + (void) lpszOutput; + (void) lpInitData; + (void) lpszDevice; + if (strcmp(lpszDriver, "DISPLAY")) + return NULL; + return (HDC*)16; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */ +BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags) +{ + (void) dwFlags; + if (lpDevice != NULL) + { + fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n"); + abort(); + return FALSE; + } + if (iDevNum >= 2) + return FALSE; + if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) + { + fprintf(stderr, + "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n"); + abort(); + return FALSE; + } + strcmp(lpDisplayDevice->DeviceName, "some monitor"); + lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE; + return TRUE; +} + +#else + + +#include +#include + + +#define GAMMA_RAMP_SIZE 256 + + +static xcb_connection_t* connection = NULL; +static size_t dc_count = 0; +static ssize_t crtc_count = -1; +static xcb_randr_crtc_t* crtcs = NULL; +static xcb_randr_get_screen_resources_current_reply_t* res_reply = NULL; + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */ +HDC GetDC(HWND hWnd) +{ + (void) hWnd; + return CreateDC(TEXT("DISPLAY"), "0", NULL, NULL); +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */ +int ReleaseDC(HWND hWnd, HDC hDC) +{ + (void) hWnd; + (void) hDC; + dc_count--; + if (dc_count == 0) + { + if (connection != NULL) + xcb_disconnect(connection); + connection = NULL; + free(res_reply); + res_reply = NULL; + } + return 1; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */ +int GetDeviceCaps(HDC hDC, int nIndex) +{ + (void) hDC; + return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */ +BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + xcb_void_cookie_t gamma_cookie = + xcb_randr_set_crtc_gamma_checked(connection, *(xcb_randr_crtc_t*)hDC, GAMMA_RAMP_SIZE, + ((uint16_t*)lpRamp) + 0 * GAMMA_RAMP_SIZE, + ((uint16_t*)lpRamp) + 1 * GAMMA_RAMP_SIZE, + ((uint16_t*)lpRamp) + 2 * GAMMA_RAMP_SIZE); + xcb_generic_error_t* error = xcb_request_check(connection, gamma_cookie); + return error == NULL ? TRUE : FALSE; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */ +BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + xcb_randr_get_crtc_gamma_cookie_t gamma_cookie; + xcb_randr_get_crtc_gamma_reply_t* gamma_reply; + xcb_generic_error_t* error; + + gamma_cookie = xcb_randr_get_crtc_gamma(connection, *(xcb_randr_crtc_t*)hDC); + gamma_reply = xcb_randr_get_crtc_gamma_reply(connection, gamma_cookie, &error); + + if (error) + return FALSE; + +#define DEST_RAMP(I) (((uint16_t*)lpRamp) + (I) * GAMMA_RAMP_SIZE) +#define SRC_RAMP(C) (xcb_randr_get_crtc_gamma_##C(gamma_reply)) + + memcpy(DEST_RAMP(0), SRC_RAMP(red), GAMMA_RAMP_SIZE * sizeof(uint16_t)); + memcpy(DEST_RAMP(1), SRC_RAMP(green), GAMMA_RAMP_SIZE * sizeof(uint16_t)); + memcpy(DEST_RAMP(2), SRC_RAMP(blue), GAMMA_RAMP_SIZE * sizeof(uint16_t)); + +#undef SRC_RAMP +#undef DEST_RAMP + + free(gamma_reply); + return TRUE; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */ +HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void *lpszOutput, void *lpInitData) +{ + int crtc_index = atoi(lpszDevice); + + (void) lpszOutput; + (void) lpInitData; + + if (strcmp(lpszDriver, "DISPLAY")) + return NULL; + + if (dc_count == 0) { + xcb_generic_error_t* error; + xcb_screen_iterator_t iter; + xcb_randr_get_screen_resources_current_cookie_t res_cookie; + + connection = xcb_connect(NULL, NULL); + + iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); + res_cookie = xcb_randr_get_screen_resources_current(connection, iter.data->root); + res_reply = xcb_randr_get_screen_resources_current_reply(connection, res_cookie, &error); + + if (error) + { + xcb_disconnect(connection); + crtc_count = -1; + return NULL; + } + + crtc_count = res_reply->num_crtcs; + crtcs = xcb_randr_get_screen_resources_current_crtcs(res_reply); + } + + if (crtc_index >= crtc_count) + { + if (dc_count == 0) + { + xcb_disconnect(connection); + crtc_count = -1; + } + return NULL; + } + + dc_count++; + return crtcs + crtc_index; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */ +BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags) +{ + size_t count = (size_t)crtc_count; + (void) dwFlags; + if (lpDevice != NULL) + { + fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n"); + abort(); + return FALSE; + } + if (crtc_count < 0) + { + if (GetDC(NULL) == NULL) + return FALSE; + dc_count = 0; + count = (size_t)crtc_count; + ReleaseDC(NULL, NULL); + } + if (iDevNum >= count) + return FALSE; + if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) + { + fprintf(stderr, "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n"); + abort(); + return FALSE; + } + sprintf(lpDisplayDevice->DeviceName, "%i", iDevNum); + lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE; + return TRUE; +} + + +#endif + diff --git a/src/fake-w32-gdi.h b/src/fake-w32-gdi.h new file mode 100644 index 0000000..0648b76 --- /dev/null +++ b/src/fake-w32-gdi.h @@ -0,0 +1,80 @@ +/** + * libgamma — Display server abstraction layer for gamma ramp adjustments + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library. If not, see . + */ +#ifndef LIBGAMMA_FAKE_W32_GDI_H +#define LIBGAMMA_FAKE_W32_GDI_H + +#ifndef HAVE_GAMMA_METHOD_W32_GDI +# error Including fake-w32-gdi.h without FAKE_GAMMA_METHOD_W32_GDI +#endif + + +#include + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx */ +typedef uint16_t WORD; +typedef uint32_t DWORD; +typedef int BOOL; +typedef void* HDC; +typedef void* HWND; +typedef void* LPVOID; +typedef const char* LPCTSTR; +typedef char TCHAR; +#define TRUE 1 +#define FALSE 0 + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */ +HDC GetDC(HWND hWnd); + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */ +int ReleaseDC(HWND hWnd, HDC hDC); + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */ +int GetDeviceCaps(HDC hDC, int nIndex) __attribute__((const)); +#define COLORMGMTCAPS 1 +#define CM_GAMMA_RAMP 1 + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */ +BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp); + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */ +BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp); + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */ +HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void* lpszOutput, void* lpInitData); +#define TEXT(X) ((LPCTSTR)(X)) + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183569(v=vs.85).aspx */ +typedef struct +{ + DWORD cb; + TCHAR DeviceName[32]; + DWORD StateFlags; +} DISPLAY_DEVICE; +typedef DISPLAY_DEVICE* PDISPLAY_DEVICE; +#define DISPLAY_DEVICE_ACTIVE 1 + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */ +BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags); + + +#endif + diff --git a/src/gamma-dummy.h b/src/gamma-dummy.h index ae0b4f9..e7733f5 100644 --- a/src/gamma-dummy.h +++ b/src/gamma-dummy.h @@ -18,7 +18,6 @@ #ifndef LIBGAMMA_GAMMA_DUMMY_H #define LIBGAMMA_GAMMA_DUMMY_H - #ifndef HAVE_GAMMA_METHOD_DUMMY # error Including gamma-dummy.h without HAVE_GAMMA_METHOD_DUMMY #endif diff --git a/src/gamma-linux-drm.h b/src/gamma-linux-drm.h index 144e45d..2f757bf 100644 --- a/src/gamma-linux-drm.h +++ b/src/gamma-linux-drm.h @@ -18,7 +18,6 @@ #ifndef LIBGAMMA_GAMMA_LINUX_DRM_H #define LIBGAMMA_GAMMA_LINUX_DRM_H - #ifndef HAVE_GAMMA_METHOD_LINUX_DRM # error Including gamma-linux-drm.h without HAVE_GAMMA_METHOD_LINUX_DRM #endif diff --git a/src/gamma-quartz-cg.c b/src/gamma-quartz-cg.c index fc0a4e2..598780f 100644 --- a/src/gamma-quartz-cg.c +++ b/src/gamma-quartz-cg.c @@ -23,6 +23,8 @@ #include "libgamma-error.h" +#include + /** * Return the capabilities of the adjustment method @@ -33,7 +35,7 @@ void libgamma_quartz_cg_method_capabilities(libgamma_method_capabilities_t* rest { this->crtc_information = CRTC_INFO_GAMMA_SIZE | CRTC_INFO_GAMMA_DEPTH; - this->default_site_known = NULL; + this->default_site_known = 1; this->multiple_sites = 0; this->multiple_partitions = 0; this->multiple_crtcs = 1; diff --git a/src/gamma-quartz-cg.h b/src/gamma-quartz-cg.h index 2bb8fb4..f4331b4 100644 --- a/src/gamma-quartz-cg.h +++ b/src/gamma-quartz-cg.h @@ -18,7 +18,6 @@ #ifndef LIBGAMMA_GAMMA_QUARTZ_CG_H #define LIBGAMMA_GAMMA_QUARTZ_CG_H - #ifndef HAVE_GAMMA_METHOD_QUARTZ_CORE_GRAPHICS # error Including gamma-quartz-cg.h without HAVE_GAMMA_METHOD_QUARTZ_CORE_GRAPHICS #endif diff --git a/src/gamma-w32-gdi.c b/src/gamma-w32-gdi.c index e6155a8..a1d8ffe 100644 --- a/src/gamma-w32-gdi.c +++ b/src/gamma-w32-gdi.c @@ -1,4 +1,4 @@ -x/** +/** * libgamma — Display server abstraction layer for gamma ramp adjustments * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) * @@ -27,12 +27,14 @@ x/** # define WINVER 0x0500 #endif #ifdef FAKE_GAMMA_METHOD_W32_GDI -# include "fake-w32-gdi.h" +# include "fake-w32-gdi.h" #else -# include -# include +# include +# include #endif +#include + #define GAMMA_RAMP_SIZE 256 @@ -46,7 +48,7 @@ void libgamma_w32_gdi_method_capabilities(libgamma_method_capabilities_t* restri this->crtc_information = CRTC_INFO_GAMMA_SIZE | CRTC_INFO_GAMMA_DEPTH | CRTC_INFO_GAMMA_SUPPORT; - this->default_site_known = NULL; + this->default_site_known = 1; this->multiple_sites = 0; this->multiple_partitions = 0; this->multiple_crtcs = 1; @@ -133,6 +135,7 @@ int libgamma_w32_gdi_site_restore(libgamma_site_state_t* restrict this) int libgamma_w32_gdi_partition_initialise(libgamma_partition_state_t* restrict this, libgamma_site_state_t* restrict site, size_t partition) { + DWORD n = 0; DISPLAY_DEVICE display; (void) site; @@ -140,16 +143,11 @@ int libgamma_w32_gdi_partition_initialise(libgamma_partition_state_t* restrict t if (partition != 0) return LIBGAMMA_NO_SUCH_PARTITION; - this->crtcs_available = 0; display.cb = sizeof(DISPLAY_DEVICE); - for (;;) - { - if (!EnumDisplayDevices(NULL, this->crtcs_available, &display, 0)) - break; - this->crtcs_available++; - if (this->crtcs_available == 0) - return LIBGAMMA_IMPOSSIBLE_AMOUNT; - } + while (EnumDisplayDevices(NULL, n, &display, 0)) + if (n++, n == 0) + return LIBGAMMA_IMPOSSIBLE_AMOUNT; + this->crtcs_available = (size_t)n; return 0; } @@ -199,7 +197,7 @@ int libgamma_w32_gdi_crtc_initialise(libgamma_crtc_state_t* restrict this, this->data = NULL; display.cb = sizeof(DISPLAY_DEVICE); - if (!EnumDisplayDevices(NULL, crtc, &display, 0)) + if (!EnumDisplayDevices(NULL, (DWORD)crtc, &display, 0)) return LIBGAMMA_NO_SUCH_CRTC; if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) return LIBGAMMA_CONNECTOR_DISABLED; @@ -256,14 +254,14 @@ int libgamma_w32_gdi_get_crtc_information(libgamma_crtc_information_t* restrict this->height_mm_error = _E(CRTC_INFO_HEIGHT_MM); this->width_mm_edid_error = _E(CRTC_INFO_WIDTH_MM_EDID); this->height_mm_edid_error = _E(CRTC_INFO_HEIGHT_MM_EDID); - this->red_gamma_size = GAMMA_SIZE; - this->green_gamma_size = GAMMA_SIZE; - this->blue_gamma_size = GAMMA_SIZE; + this->red_gamma_size = GAMMA_RAMP_SIZE; + this->green_gamma_size = GAMMA_RAMP_SIZE; + this->blue_gamma_size = GAMMA_RAMP_SIZE; this->gamma_size_error = 0; this->gamma_depth = 16; this->gamma_depth_error = 0; if ((fields & CRTC_INFO_GAMMA_SUPPORT)) - this->gamma_support = GetDeviceCaps(hDC, COLORMGMTCAPS) == CM_GAMMA_RAMP; + this->gamma_support = GetDeviceCaps(crtc->data, COLORMGMTCAPS) == CM_GAMMA_RAMP; this->gamma_support_error = 0; this->subpixel_order_error = _E(CRTC_INFO_SUBPIXEL_ORDER); this->active_error = _E(CRTC_INFO_ACTIVE); diff --git a/src/gamma-w32-gdi.h b/src/gamma-w32-gdi.h index 7bf8356..02c220e 100644 --- a/src/gamma-w32-gdi.h +++ b/src/gamma-w32-gdi.h @@ -18,7 +18,6 @@ #ifndef LIBGAMMA_GAMMA_W32_GDI_H #define LIBGAMMA_GAMMA_W32_GDI_H - #ifndef HAVE_GAMMA_METHOD_W32_GDI # error Including gamma-w32-gdi.h without HAVE_GAMMA_METHOD_W32_GDI #endif diff --git a/src/gamma-x-randr.h b/src/gamma-x-randr.h index 3091bf5..0ec432d 100644 --- a/src/gamma-x-randr.h +++ b/src/gamma-x-randr.h @@ -18,7 +18,6 @@ #ifndef LIBGAMMA_GAMMA_X_RANDR_H #define LIBGAMMA_GAMMA_X_RANDR_H - #ifndef HAVE_GAMMA_METHOD_X_RANDR # error Including gamma-x-randr.h without HAVE_GAMMA_METHOD_X_RANDR #endif diff --git a/src/gamma-x-vidmode.h b/src/gamma-x-vidmode.h index 266d137..396fd7a 100644 --- a/src/gamma-x-vidmode.h +++ b/src/gamma-x-vidmode.h @@ -18,7 +18,6 @@ #ifndef LIBGAMMA_GAMMA_X_VIDMODE_H #define LIBGAMMA_GAMMA_X_VIDMODE_H - #ifndef HAVE_GAMMA_METHOD_X_VIDMODE # error Including gamma-x-vidmode.h without HAVE_GAMMA_METHOD_X_VIDMODE #endif diff --git a/src/libgamma-method.h b/src/libgamma-method.h index c3fb36a..f9d625a 100644 --- a/src/libgamma-method.h +++ b/src/libgamma-method.h @@ -113,8 +113,8 @@ /** * Capabilities of adjustment methods */ -typedef struct libgamma_method_capabilities { - +typedef struct libgamma_method_capabilities +{ /** * OR of the CRTC information fields in `libgamma_crtc_information_t` * that may (but can fail) be read successfully @@ -207,8 +207,8 @@ typedef struct libgamma_method_capabilities { * and the BSD:s, there can usually be any (feasible) number of * sites. In X.org parlance they are called displays. */ -typedef struct libgamma_site_state { - +typedef struct libgamma_site_state +{ /** * Adjustment method implementation specific data. * You as a user of this library should not touch this. @@ -259,8 +259,8 @@ typedef struct libgamma_site_state { * On hardware-level adjustment methods, such as Direct * Rendering Manager, a partition is a graphics card. */ -typedef struct libgamma_partition_state { - +typedef struct libgamma_partition_state +{ /** * Adjustment method implementation specific data. * You as a user of this library should not touch this. @@ -294,8 +294,8 @@ typedef struct libgamma_partition_state { * monitor that is plugged in to the connector * that the CRTC belongs to */ -typedef struct libgamma_crtc_state { - +typedef struct libgamma_crtc_state +{ /** * Adjustment method implementation specific data. * You as a user of this library should not touch this. @@ -400,8 +400,8 @@ typedef struct libgamma_crtc_state { /** * Cathode ray tube controller information data structure */ -typedef struct libgamma_crtc_information { - +typedef struct libgamma_crtc_information +{ /** * The Extended Display Identification Data associated with * the attached monitor. This is raw byte array that is usually -- cgit v1.2.3-70-g09d2