diff --git a/net-misc/freerdp/freerdp-2.0.0~git.recipe b/net-misc/freerdp/freerdp-2.4.1.recipe similarity index 72% rename from net-misc/freerdp/freerdp-2.0.0~git.recipe rename to net-misc/freerdp/freerdp-2.4.1.recipe index 902ce9e3f..c8e5d62e1 100644 --- a/net-misc/freerdp/freerdp-2.0.0~git.recipe +++ b/net-misc/freerdp/freerdp-2.4.1.recipe @@ -1,17 +1,16 @@ SUMMARY="Free implementation of the Remote Desktop Protocol" DESCRIPTION="FreeRDP is a free implementation of the Remote Desktop Protocol (RDP), \ -released under the Apache license. -Enjoy the freedom of using your software wherever you want, the way you want it, in \ -a world where interoperability can finally liberate your computing experience." -HOMEPAGE="http://rdesktop.org" +released under the Apache license. Enjoy the freedom of using your software wherever \ +you want, the way you want it, in a world where interoperability can finally \ +liberate your computing experience." +HOMEPAGE="https://freerdp.com" COPYRIGHT="FreeRDP team" LICENSE="Apache v2" -REVISION="3" -srcGitRev="a51e5a9084917f88941a9bfe7d4c18b7afb0d750" -SOURCE_URI="https://github.com/FreeRDP/FreeRDP/archive/$srcGitRev.tar.gz" -CHECKSUM_SHA256="b5a7f3e90d05ff246dc966bbb9de1d6ec92934ef5c66c2e60da54b1e38b2cad6" -SOURCE_DIR="FreeRDP-$srcGitRev" -PATCHES="freerdp-2.0.0~git.patchset" +REVISION="1" +SOURCE_URI="https://github.com/FreeRDP/FreeRDP/releases/download/$portVersion/freerdp-$portVersion.tar.gz" +CHECKSUM_SHA256="ef75c87926643a0d0041f6556e343ac037380d4260c64885e7cdd20da0147edf" +SOURCE_DIR="freerdp-$portVersion" +PATCHES="freerdp-$portVersion.patchset" ARCHITECTURES="all !x86_gcc2" SECONDARY_ARCHITECTURES="x86" @@ -33,7 +32,6 @@ REQUIRES=" lib:libavutil$secondaryArchSuffix lib:libcrypto$secondaryArchSuffix lib:libexecinfo$secondaryArchSuffix - lib:libglib_2.0$secondaryArchSuffix lib:libjpeg$secondaryArchSuffix lib:libssl$secondaryArchSuffix lib:libz$secondaryArchSuffix @@ -58,7 +56,6 @@ BUILD_REQUIRES=" devel:libavutil$secondaryArchSuffix devel:libcrypto$secondaryArchSuffix devel:libexecinfo$secondaryArchSuffix - devel:libglib_2.0$secondaryArchSuffix devel:libjpeg$secondaryArchSuffix devel:libssl$secondaryArchSuffix devel:libz$secondaryArchSuffix @@ -75,21 +72,17 @@ BUILD() { mkdir -p build cd build + LDFLAGS=-Wl,--no-undefined \ cmake .. $cmakeDirArgs \ - -DCMAKE_INSTALL_PREFIX:PATH=$prefix \ - -DCMAKE_INSTALL_INCLUDEDIR=$includeDir \ - -DCMAKE_INSTALL_LIBDIR=$libDir \ - -DCMAKE_INSTALL_BINDIR:PATH=$binDir \ - -DCMAKE_INSTALL_SBINDIR:PATH=$binDir \ - -DCMAKE_INSTALL_LIBEXECDIR:PATH=$binDir \ - -DCMAKE_CXX_FLAGS:STRING=-D_BSD_SOURCE \ - -DCMAKE_C_FLAGS:STRING=-D_BSD_SOURCE \ + -DCMAKE_BUILD_TYPE=Release \ -DWITH_GSTREAMER_1_0=OFF \ -DWITH_FFMPEG=ON \ + -DWITH_SWSCALE=ON \ -DWITH_JPEG=ON \ -DWITH_CUPS=OFF \ -DWITH_OSS=OFF \ -DWITH_PULSE=OFF \ + -DCHANNEL_URBDRC=OFF \ -DBUILD_SHARED_LIBS=ON make $jobArgs } diff --git a/net-misc/freerdp/patches/freerdp-2.4.1.patchset b/net-misc/freerdp/patches/freerdp-2.4.1.patchset new file mode 100644 index 000000000..9c108637e --- /dev/null +++ b/net-misc/freerdp/patches/freerdp-2.4.1.patchset @@ -0,0 +1,1340 @@ +From c3dfe6c77e59e2c845dffc544c9d2faaac8fa9ef Mon Sep 17 00:00:00 2001 +From: Augustin Cavalier +Date: Fri, 19 Nov 2021 10:58:04 -0500 +Subject: Core patches for Haiku. + +Based partially on 3dEyes' changes for 2.0.0~git but mostly my own work. + +diff --git a/winpr/include/winpr/winsock.h b/winpr/include/winpr/winsock.h +index 1677609..7912ee5 100644 +--- a/winpr/include/winpr/winsock.h ++++ b/winpr/include/winpr/winsock.h +@@ -84,6 +84,11 @@ WINPR_API INT winpr_inet_pton(INT Family, PCSTR pszAddrString, PVOID pAddrBuf); + #include + #include + ++#ifdef __HAIKU__ ++#include ++#include ++#endif ++ + #include + #include + #include +diff --git a/winpr/libwinpr/CMakeLists.txt b/winpr/libwinpr/CMakeLists.txt +index d9673f2..08e6020 100644 +--- a/winpr/libwinpr/CMakeLists.txt ++++ b/winpr/libwinpr/CMakeLists.txt +@@ -77,14 +77,17 @@ macro (winpr_definition_add) + set (WINPR_DEFINITIONS ${WINPR_DEFINITIONS} PARENT_SCOPE) + endmacro() + +-set(CMAKE_REQUIRED_LIBRARIES rt) ++if (NOT HAIKU) ++ set(RT_LIBS rt) ++endif() ++set(CMAKE_REQUIRED_LIBRARIES ${RT_LIBS}) + check_function_exists(timer_create TIMER_CREATE) + check_function_exists(timer_delete TIMER_DELETE) + check_function_exists(timer_settime TIMER_SETTIME) + check_function_exists(timer_gettime TIMER_GETTIME) + if (TIMER_CREATE AND TIMER_DELETE AND TIMER_SETTIME AND TIMER_GETTIME) + add_definitions(-DWITH_POSIX_TIMER) +- winpr_library_add_private(rt) ++ winpr_library_add_private(${RT_LIBS}) + endif() + + if (ANDROID) +diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c +index 4204f59..da78812 100644 +--- a/winpr/libwinpr/synch/timer.c ++++ b/winpr/libwinpr/synch/timer.c +@@ -854,7 +854,9 @@ static int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue) + pthread_attr_init(&(timerQueue->attr)); + timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param)); ++#ifndef __HAIKU__ + pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO); ++#endif + pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue); + return 0; + } +diff --git a/winpr/libwinpr/sysinfo/CMakeLists.txt b/winpr/libwinpr/sysinfo/CMakeLists.txt +index 799df05..18723bb 100644 +--- a/winpr/libwinpr/sysinfo/CMakeLists.txt ++++ b/winpr/libwinpr/sysinfo/CMakeLists.txt +@@ -21,7 +21,7 @@ endif() + + winpr_module_add(sysinfo.c) + +-if((NOT WIN32) AND (NOT APPLE) AND (NOT ANDROID) AND (NOT OPENBSD)) ++if((NOT WIN32) AND (NOT APPLE) AND (NOT ANDROID) AND (NOT OPENBSD) AND (NOT HAIKU)) + winpr_library_add_private(rt) + endif() + +diff --git a/winpr/libwinpr/utils/CMakeLists.txt b/winpr/libwinpr/utils/CMakeLists.txt +index 2027a60..639f490 100644 +--- a/winpr/libwinpr/utils/CMakeLists.txt ++++ b/winpr/libwinpr/utils/CMakeLists.txt +@@ -139,7 +139,7 @@ if(UNIX) + winpr_library_add_private(m) + endif() + +-if((FREEBSD) AND (NOT KFREEBSD)) ++if(((FREEBSD) AND (NOT KFREEBSD)) OR HAIKU) + winpr_library_add_private(execinfo) + endif() + +diff --git a/winpr/libwinpr/winsock/CMakeLists.txt b/winpr/libwinpr/winsock/CMakeLists.txt +index ef13cd1..8cd6a62 100644 +--- a/winpr/libwinpr/winsock/CMakeLists.txt ++++ b/winpr/libwinpr/winsock/CMakeLists.txt +@@ -20,3 +20,7 @@ winpr_module_add(winsock.c) + if(WIN32) + winpr_library_add_public(ws2_32) + endif() ++ ++if(HAIKU) ++ winpr_library_add_public(network) ++endif() +diff --git a/winpr/libwinpr/winsock/winsock.c b/winpr/libwinpr/winsock/winsock.c +index 6e82be9..d7d6613 100644 +--- a/winpr/libwinpr/winsock/winsock.c ++++ b/winpr/libwinpr/winsock/winsock.c +@@ -381,9 +381,11 @@ void WSASetLastError(int iError) + errno = EPROTONOSUPPORT; + break; + ++#ifdef ESOCKTNOSUPPORT + case WSAESOCKTNOSUPPORT: + errno = ESOCKTNOSUPPORT; + break; ++#endif + + case WSAEOPNOTSUPP: + errno = EOPNOTSUPP; +@@ -441,9 +443,11 @@ void WSASetLastError(int iError) + errno = ESHUTDOWN; + break; + ++#ifdef ETOOMANYREFS + case WSAETOOMANYREFS: + errno = ETOOMANYREFS; + break; ++#endif + + case WSAETIMEDOUT: + errno = ETIMEDOUT; +@@ -472,16 +476,18 @@ void WSASetLastError(int iError) + case WSAENOTEMPTY: + errno = ENOTEMPTY; + break; +-#ifdef EPROCLIM + ++#ifdef EPROCLIM + case WSAEPROCLIM: + errno = EPROCLIM; + break; + #endif + ++#ifdef EUSERS + case WSAEUSERS: + errno = EUSERS; + break; ++#endif + + case WSAEDQUOT: + errno = EDQUOT; +@@ -491,9 +497,11 @@ void WSASetLastError(int iError) + errno = ESTALE; + break; + ++#ifdef EREMOTE + case WSAEREMOTE: + errno = EREMOTE; + break; ++#endif + } + } + +@@ -566,9 +574,11 @@ int WSAGetLastError(void) + iError = WSAEPROTONOSUPPORT; + break; + ++#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + iError = WSAESOCKTNOSUPPORT; + break; ++#endif + + case EOPNOTSUPP: + iError = WSAEOPNOTSUPP; +@@ -626,9 +636,11 @@ int WSAGetLastError(void) + iError = WSAESHUTDOWN; + break; + ++#ifdef ETOOMANYREFS + case ETOOMANYREFS: + iError = WSAETOOMANYREFS; + break; ++#endif + + case ETIMEDOUT: + iError = WSAETIMEDOUT; +@@ -657,16 +669,18 @@ int WSAGetLastError(void) + case ENOTEMPTY: + iError = WSAENOTEMPTY; + break; +-#ifdef EPROCLIM + ++#ifdef EPROCLIM + case EPROCLIM: + iError = WSAEPROCLIM; + break; + #endif + ++#ifdef EUSERS + case EUSERS: + iError = WSAEUSERS; + break; ++#endif + + case EDQUOT: + iError = WSAEDQUOT; +@@ -676,18 +690,19 @@ int WSAGetLastError(void) + iError = WSAESTALE; + break; + ++#ifdef EREMOTE + case EREMOTE: + iError = WSAEREMOTE; + break; ++#endif ++ + /* Special cases */ + #if (EAGAIN != EWOULDBLOCK) +- + case EAGAIN: + iError = WSAEWOULDBLOCK; + break; + #endif + #if defined(EPROTO) +- + case EPROTO: + iError = WSAECONNRESET; + break; +diff --git a/winpr/winpr.pc.in b/winpr/winpr.pc.in +index 6b0c950..f94f8f9 100644 +--- a/winpr/winpr.pc.in ++++ b/winpr/winpr.pc.in +@@ -11,5 +11,5 @@ Version: @WINPR_VERSION@ + Requires: + Requires.private: libssl + Libs: -L${libdir} ${libs} +-Libs.private: -ldl -lrt -lm -lpthread ++Libs.private: -ldl -lnetwork -lm -lpthread + Cflags: -I${includedir} +-- +2.30.2 + + +From 3915d673cc0a6a19709abae94c66d4555eb79d6c Mon Sep 17 00:00:00 2001 +From: Augustin Cavalier +Date: Fri, 19 Nov 2021 12:19:47 -0500 +Subject: shell: Do not try to make directories that already exist. + +This causes all directory creation in ~/settings to fail, +as otherwise it tries to make ~/settings which already exists, +but fails with EROFS and not EEXIST due to packagefs. + +diff --git a/winpr/libwinpr/path/shell.c b/winpr/libwinpr/path/shell.c +index 4bba4ec..b1db9d4 100644 +--- a/winpr/libwinpr/path/shell.c ++++ b/winpr/libwinpr/path/shell.c +@@ -507,10 +507,24 @@ BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes) + for (p = dup; p;) + #endif + { ++ struct stat stat_info; ++ BOOL skip = FALSE; ++ + if ((p = strchr(p + 1, delim))) + *p = '\0'; + +- if (mkdir(dup, 0777) != 0) ++ if (stat(dup, &stat_info) == 0) ++ { ++ if ((stat_info.st_mode & S_IFDIR) == 0) ++ { ++ result = FALSE; ++ break; ++ } ++ ++ skip = TRUE; ++ } ++ ++ if (!skip && mkdir(dup, 0777) != 0) + if (errno != EEXIST) + { + result = FALSE; +-- +2.30.2 + + +From 7c250e9055fc383b2d2522a58e496f7eb40a244f Mon Sep 17 00:00:00 2001 +From: Gerasim Troeglazov <3dEyes@gmail.com> +Date: Fri, 19 Nov 2021 12:27:00 -0500 +Subject: Add Haiku client. + +Mostly 3dEyes' code from 2.0.0~git; various fixes by me +around new RDP APIs. + +diff --git a/client/.gitignore b/client/.gitignore +index 7c1ea95..b78cb50 100644 +--- a/client/.gitignore ++++ b/client/.gitignore +@@ -1,6 +1,7 @@ + /* + !/Android + !/common ++!/Haiku + !/iOS + !/Mac + !/Sample +diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt +index 7cba44e..d339fe2 100644 +--- a/client/CMakeLists.txt ++++ b/client/CMakeLists.txt +@@ -51,6 +51,11 @@ if(FREERDP_VENDOR AND WITH_CLIENT) + message(STATUS "Adding Android client") + add_subdirectory(Android) + endif() ++ ++ if(HAIKU) ++ message(STATUS "Adding Haiku client") ++ add_subdirectory(Haiku) ++ endif() + endif() + + # Pick up other clients +diff --git a/client/Haiku/CMakeLists.txt b/client/Haiku/CMakeLists.txt +new file mode 100644 +index 0000000..4df9959 +--- /dev/null ++++ b/client/Haiku/CMakeLists.txt +@@ -0,0 +1,36 @@ ++# FreeRDP: A Remote Desktop Protocol Implementation ++# FreeRDP Haiku UI cmake build script ++# ++# Copyright 2018 Gerasim Troeglazov <3dEyes@gmail.com> ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++set(MODULE_NAME "FreeRDP") ++set(MODULE_PREFIX "FREERDP_CLIENT_HAIKU") ++set(CMAKE_POSITION_INDEPENDENT_CODE OFF) ++ ++set(${MODULE_PREFIX}_SRCS ++ FreeRDP.cpp MainWindow.cpp MainView.cpp) ++ ++add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) ++ ++set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${CMAKE_DL_LIBS}) ++set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client freerdp) ++set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} be network media translation) ++target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) ++ ++ADD_CUSTOM_COMMAND(TARGET ${MODULE_NAME} COMMAND rc -o Resources.rsrc ${CMAKE_CURRENT_SOURCE_DIR}/Resources.rdef COMMENT "Compiling resources") ++ADD_CUSTOM_COMMAND(TARGET ${MODULE_NAME} COMMAND xres -o ${MODULE_NAME} Resources.rsrc COMMENT "Adding resources to executable") ++ADD_CUSTOM_COMMAND(TARGET ${MODULE_NAME} COMMAND mimeset --all ${MODULE_NAME} COMMENT "Adjusting MIME types") ++ ++set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Haiku") +diff --git a/client/Haiku/FreeRDP.cpp b/client/Haiku/FreeRDP.cpp +new file mode 100644 +index 0000000..6cc92d4 +--- /dev/null ++++ b/client/Haiku/FreeRDP.cpp +@@ -0,0 +1,360 @@ ++/** ++ * FreeRDP: A Remote Desktop Protocol Implementation ++ * FreeRDP Haiku UI ++ * ++ * Copyright 2018 Gerasim Troeglazov ++ * Copyright 2021 Haiku, Inc. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "MainWindow.h" ++#include "MainView.h" ++ ++#include ++#include ++#include ++#include ++ ++extern "C" { ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++} ++ ++#define TAG CLIENT_TAG("haiku") ++ ++MainWindow *wnd = NULL; ++ ++struct tf_context ++{ ++ rdpContext _p; ++}; ++typedef struct tf_context tfContext; ++ ++static int tf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type) ++{ ++ const char* str_data = freerdp_get_logon_error_info_data(data); ++ const char* str_type = freerdp_get_logon_error_info_type(type); ++ ++ if (!instance || !instance->context) ++ return -1; ++ ++ WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type); ++ return 1; ++} ++ ++static BOOL tf_begin_paint(rdpContext* context) ++{ ++ rdpGdi* gdi = context->gdi; ++ gdi->primary->hdc->hwnd->invalid->null = TRUE; ++ return TRUE; ++} ++ ++static BOOL tf_end_paint(rdpContext* context) ++{ ++ rdpGdi* gdi = context->gdi; ++ ++ if(wnd==NULL) ++ return TRUE; ++ ++ if (gdi->primary->hdc->hwnd->invalid->null) ++ return TRUE; ++ ++ char* data = (char*)wnd->fb->GetBuffer(); ++ ++ int x = gdi->primary->hdc->hwnd->invalid->x; ++ int y = gdi->primary->hdc->hwnd->invalid->y; ++ int w = gdi->primary->hdc->hwnd->invalid->w; ++ int h = gdi->primary->hdc->hwnd->invalid->h; ++ ++ for (int i = 0; i < h; i++) ++ { ++ memcpy(data + ((i + y) * (gdi->width * GetBytesPerPixel( ++ gdi->dstFormat))) + x * GetBytesPerPixel(gdi->dstFormat), ++ gdi->primary_buffer + ((i + y) * (gdi->width * GetBytesPerPixel( ++ gdi->dstFormat))) + x * GetBytesPerPixel(gdi->dstFormat), ++ w * GetBytesPerPixel(gdi->dstFormat)); ++ } ++ ++ wnd->fb->Paint(); ++ ++ return TRUE; ++} ++ ++static BOOL tf_pre_connect(freerdp* instance) ++{ ++ rdpSettings* settings; ++ settings = instance->settings; ++ settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; ++ settings->OrderSupport[NEG_PATBLT_INDEX] = TRUE; ++ settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE; ++ settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; ++ settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; ++ settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; ++ settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; ++ settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; ++ settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = TRUE; ++ settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; ++ settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; ++ settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; ++ settings->OrderSupport[NEG_MEMBLT_INDEX] = TRUE; ++ settings->OrderSupport[NEG_MEM3BLT_INDEX] = FALSE; ++ settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; ++ settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = FALSE; ++ settings->OrderSupport[NEG_FAST_INDEX_INDEX] = FALSE; ++ settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = FALSE; ++ settings->OrderSupport[NEG_POLYGON_SC_INDEX] = FALSE; ++ settings->OrderSupport[NEG_POLYGON_CB_INDEX] = FALSE; ++ settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; ++ settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; ++ ++ if (settings->Fullscreen) { ++ BScreen screen; ++ settings->DesktopWidth = (UINT32) screen.Frame().right + 1; ++ settings->DesktopHeight = (UINT32) screen.Frame().bottom + 1; ++ } ++ ++ return TRUE; ++} ++ ++static BOOL tf_post_connect(freerdp* instance) ++{ ++ rdpGdi* gdi; ++ rdpContext* context; ++ rdpSettings* settings; ++ ++ context = ((rdpContext*) instance->context); ++ ++ if (!gdi_init(instance, PIXEL_FORMAT_BGRA32)) ++ return FALSE; ++ ++ gdi = instance->context->gdi; ++ settings = instance->settings; ++ ++ instance->update->BeginPaint = tf_begin_paint; ++ instance->update->EndPaint = tf_end_paint; ++ ++ if(wnd==NULL) { ++ char wndTitle[256]={"FreeRDP"}; ++ ++ BRect rect = BRect(50, 50, 50 + gdi->width, 50 + gdi->height); ++ if (settings->Fullscreen) { ++ rect = BRect(0, 0, gdi->width, gdi->height); ++ } ++ if (settings->WindowTitle != NULL) ++ snprintf(wndTitle, 255, "%s", settings->WindowTitle); ++ else if (settings->ServerPort == 3389) ++ snprintf(wndTitle, 255, "FreeRDP: %s", settings->ServerHostname); ++ else ++ snprintf(wndTitle, 255, "FreeRDP: %S:%u", settings->ServerHostname, settings->ServerPort); ++ ++ wnd = new MainWindow(rect, wndTitle, instance); ++ wnd->Show(); ++ } ++ return TRUE; ++} ++ ++static void tf_post_disconnect(freerdp* instance) ++{ ++ rdpContext* context; ++ ++ if (!instance) ++ return; ++ ++ if (!instance->context) ++ return; ++ ++ context = (rdpContext*) instance->context; ++ gdi_free(instance); ++ ++ if (wnd!=NULL) { ++ wnd->Lock(); ++ wnd->Quit(); ++ } ++} ++ ++static DWORD WINAPI tf_haiku_app_thread_proc(LPVOID arg) ++{ ++ BApplication app("application/x-vnd.freerdp"); ++ *(BOOL*)arg = TRUE; ++ app.Run(); ++ ++ ExitThread(0); ++ return 0; ++} ++ ++ ++static DWORD WINAPI tf_client_thread_proc(LPVOID arg) ++{ ++ rdpContext* context = (rdpContext*)arg; ++ freerdp* instance = context->instance; ++ DWORD nCount; ++ DWORD status; ++ HANDLE handles[64]; ++ ++ if (!freerdp_connect(instance)) ++ { ++ WLog_ERR(TAG, "connection failure"); ++ return 0; ++ } ++ ++ while (!freerdp_shall_disconnect(instance)) ++ { ++ nCount = freerdp_get_event_handles(instance->context, &handles[0], 64); ++ ++ if (nCount == 0) ++ { ++ WLog_ERR(TAG, "%s: freerdp_get_event_handles failed", __FUNCTION__); ++ break; ++ } ++ ++ status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE); ++ ++ if (status == WAIT_FAILED) ++ { ++ WLog_ERR(TAG, "%s: WaitForMultipleObjects failed with %" PRIu32"", __FUNCTION__, ++ status); ++ break; ++ } ++ ++ if (!freerdp_check_event_handles(instance->context)) ++ { ++ if (client_auto_reconnect(instance)) ++ continue; ++ else ++ { ++ if (freerdp_error_info(instance) == 0) ++ status = 42; ++ } ++ ++ if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS) ++ WLog_ERR(TAG, "Failed to check FreeRDP file descriptor"); ++ ++ break; ++ } ++ } ++ ++ freerdp_disconnect(instance); ++ ExitThread(0); ++ return 0; ++} ++ ++ ++static BOOL tf_client_new(freerdp* instance, rdpContext* context) ++{ ++ tfContext* tf = (tfContext*)context; ++ ++ if (!instance || !context) ++ return FALSE; ++ ++ instance->PreConnect = tf_pre_connect; ++ instance->PostConnect = tf_post_connect; ++ instance->PostDisconnect = tf_post_disconnect; ++ instance->Authenticate = client_cli_authenticate; ++ instance->GatewayAuthenticate = client_cli_gw_authenticate; ++ instance->VerifyCertificateEx = client_cli_verify_certificate_ex; ++ instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex; ++ instance->PresentGatewayMessage = client_cli_present_gateway_message; ++ instance->LogonErrorInfo = tf_logon_error_info; ++ ++ return TRUE; ++} ++ ++ ++static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints) ++{ ++ ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS)); ++ pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION; ++ pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1); ++ pEntryPoints->ContextSize = sizeof(tfContext); ++ pEntryPoints->ClientNew = tf_client_new; ++ return 0; ++} ++ ++ ++int main(int argc, char* argv[]) ++{ ++ int rc = -1; ++ int status; ++ HANDLE appThread, clientThread; ++ RDP_CLIENT_ENTRY_POINTS clientEntryPoints; ++ rdpContext* context; ++ rdpSettings* settings; ++ BOOL appThreadReady = FALSE; ++ ++ RdpClientEntry(&clientEntryPoints); ++ context = freerdp_client_context_new(&clientEntryPoints); ++ if (!context) ++ goto fail; ++ settings = context->settings; ++ ++ status = freerdp_client_settings_parse_command_line(settings, argc, argv, FALSE); ++ if (status) ++ { ++ rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv); ++ goto fail; ++ } ++ ++ if (!(appThread = CreateThread(NULL, 0, tf_haiku_app_thread_proc, &appThreadReady, 0, NULL))) ++ { ++ WLog_ERR(TAG, "Failed to create app thread"); ++ } ++ while (!appThreadReady) ++ sched_yield(); ++ ++ if (freerdp_client_start(context) != 0) ++ goto fail; ++ ++ if (!(clientThread = CreateThread(NULL, 0, tf_client_thread_proc, context, 0, NULL))) ++ { ++ WLog_ERR(TAG, "Failed to create client thread"); ++ } ++ else ++ { ++ WaitForSingleObject(clientThread, INFINITE); ++ } ++ ++ if (freerdp_client_stop(context) != 0) ++ rc = -1; ++ else ++ rc = 0; ++ ++fail: ++ freerdp_client_context_free(context); ++ return rc; ++} +diff --git a/client/Haiku/MainView.cpp b/client/Haiku/MainView.cpp +new file mode 100644 +index 0000000..9dd50b2 +--- /dev/null ++++ b/client/Haiku/MainView.cpp +@@ -0,0 +1,235 @@ ++/* ++ * Copyright 2021 Gerasim Troeglazov. All rights reserved. ++ * Distributed under the terms of the MIT license. ++ */ ++ ++#include "MainWindow.h" ++#include "MainView.h" ++ ++ ++FBView::FBView(BRect rect) : ++ BView(rect, "FBView", B_FOLLOW_ALL, B_WILL_DRAW|B_PULSE_NEEDED|B_FRAME_EVENTS), ++ showBar(false) ++{ ++ FBView(rect, rect.IntegerWidth(), rect.IntegerHeight()); ++} ++ ++FBView::FBView(BRect rect, int width, int height) : ++ BView(rect, "FBView", B_FOLLOW_ALL, B_WILL_DRAW|B_PULSE_NEEDED|B_FRAME_EVENTS), ++ showBar(false) ++{ ++ buffer_width = width; ++ buffer_height = height; ++ lastButtons = 0; ++ ++ BRect fbRect = BRect(0,0,buffer_width-1,buffer_height-1); ++ bufferView = new BView(fbRect, "bufferView", B_FOLLOW_ALL_SIDES, 0); ++ bufferBitmap = new BBitmap(fbRect, B_RGB32, true); ++ bufferBitmap->AddChild(bufferView); ++ ++ font = be_bold_font; ++ font.SetSize(14); ++ SetFont(&font, B_FONT_ALL); ++ ++ title = BString("FreeRDP"); ++ ++ //printf("BTranslationUtils::GetBitmap\n"); ++ //closeBitmap = BTranslationUtils::GetBitmapFile("/NoIndex64/Public/FreeRDP/client/Haiku/images/close.png"); ++ //printf("W:%f H:%f\n", closeBitmap->Bounds().Width(), closeBitmap->Bounds().Height()); ++ //zoomBitmap = new BBitmap(BRect(0,0,16,16),B_RGB32);//BTranslationUtils::GetBitmap(B_PNG_FORMAT, "zoom.png"); ++} ++ ++FBView::~FBView() ++{ ++ //delete closeBitmap; ++ //delete zoomBitmap; ++} ++ ++void ++FBView::AttachedToWindow(void) ++{ ++ tickCount = 0; ++} ++ ++void ++FBView::Pulse(void) ++{ ++ uint32 dx = Bounds().Width() / 3; ++ uint32 h = showBar?20:2; ++ if (lastPos.y >= 0 && lastPos.y < h && lastPos.x > dx && lastPos.x < dx * 2 ) { ++ tickCount++; ++ if(tickCount > 5) { ++ showBar = true; ++ Invalidate(); ++ } ++ return; ++ } ++ if(showBar) { ++ showBar = false; ++ Invalidate(); ++ } ++ tickCount = 0; ++} ++ ++void ++FBView::SetInstance(freerdp *inst) ++{ ++ instance = inst; ++ title.SetTo(Window()->Title()); ++} ++ ++BPoint ++FBView::GetLastMousePos() ++{ ++ return lastPos; ++} ++ ++void FBView::MouseMoved(BPoint p, uint32 transit,const BMessage *message) ++{ ++ rdpInput* input = instance->input; ++ switch(transit) ++ { ++ case B_INSIDE_VIEW: ++ case B_ENTERED_VIEW: ++ { ++ input->MouseEvent(input, PTR_FLAGS_MOVE, p.x, p.y); ++ tickCount = 0; ++ break; ++ } ++ case B_EXITED_VIEW: ++ break; ++ case B_OUTSIDE_VIEW: ++ break; ++ } ++ lastPos = p; ++} ++ ++void FBView::MouseDown(BPoint p) ++{ ++ rdpInput* input = instance->input; ++ ++ uint32 dx = Bounds().Width() / 3; ++ uint32 h = showBar?20:2; ++ ++ uint32 buttons = Window()->CurrentMessage()->FindInt32("buttons"); ++ int32 clicks = Window()->CurrentMessage()->FindInt32("clicks"); ++ int flags = PTR_FLAGS_DOWN; ++ ++ if (buttons & B_PRIMARY_MOUSE_BUTTON) ++ flags |= PTR_FLAGS_BUTTON1; ++ if (buttons & B_SECONDARY_MOUSE_BUTTON) ++ flags |= PTR_FLAGS_BUTTON2; ++ if (buttons & B_TERTIARY_MOUSE_BUTTON) ++ flags |= PTR_FLAGS_BUTTON2; ++ ++ if (showBar && buttons & B_PRIMARY_MOUSE_BUTTON) { ++ BRect closeButtonRect(dx + 3, 3, dx + h - 3, h - 3); ++ if (closeButtonRect.Contains(p)) { ++ freerdp_abort_connect(instance); ++ } else if(clicks == 2) { ++ Window()->Minimize(true); ++ flags = 0; ++ } ++ } ++ ++ if (flags != 0) ++ input->MouseEvent(input, flags, p.x, p.y); ++ ++ lastPos = p; ++ lastButtons = buttons; ++} ++ ++void FBView::MouseUp(BPoint p) ++{ ++ rdpInput* input = instance->input; ++ ++ uint32 buttons = Window()->CurrentMessage()->FindInt32("buttons"); ++ int flags = 0; ++ ++ if ((buttons ^ lastButtons) & B_PRIMARY_MOUSE_BUTTON) ++ flags |= PTR_FLAGS_BUTTON1; ++ if ((buttons ^ lastButtons) & B_SECONDARY_MOUSE_BUTTON) ++ flags |= PTR_FLAGS_BUTTON2; ++ if ((buttons ^lastButtons) & B_TERTIARY_MOUSE_BUTTON) ++ flags |= PTR_FLAGS_BUTTON2; ++ ++ if (flags != 0) ++ input->MouseEvent(input, flags, p.x, p.y); ++ ++ lastPos = p; ++ lastButtons = buttons; ++} ++ ++void ++FBView::Draw(BRect r) ++{ ++ uint32 dx = Bounds().Width() / 3; ++ uint32 h = showBar?20:2; ++ ++ DrawBitmap(bufferBitmap, r, r); ++ if (showBar) { ++ BRect r(dx, -1, dx * 2, h); ++ SetDrawingMode(B_OP_COPY); ++ ++ SetHighColor(255, 203, 0); ++ SetLowColor(255, 203, 0); ++ FillRect(r); ++ SetHighColor(179, 143, 0); ++ SetLowColor(179, 143, 0); ++ StrokeRect(r); ++ ++ SetDrawingMode(B_OP_ALPHA); ++ BRect closeButtonRect(dx + 3, 3, dx + h - 3, h - 3); ++ StrokeRect(closeButtonRect); ++ //DrawBitmap(closeBitmap, BPoint(dx + 3, 3)); ++ ++ SetHighColor(0, 0, 0); ++ SetLowColor(0, 0, 0); ++ float width = font.StringWidth(title); ++ DrawString(title, BPoint(Bounds().Width()/2 - width/2, 16)); ++ } ++} ++ ++void ++FBView::Paint() ++{ ++ if(LockLooper()) { ++ bufferView->LockLooper(); ++ SetDrawingMode(B_OP_COPY); ++ DrawBitmap(bufferBitmap);//, bufferView->Bounds(), Bounds()); ++ bufferView->UnlockLooper(); ++ UnlockLooper(); ++ } ++} ++ ++uint32 * ++FBView::GetBuffer() ++{ ++ if(bufferBitmap) { ++ return (uint32*)bufferBitmap->Bits(); ++ } ++ return NULL; ++} ++ ++uint32 ++FBView::GetBufferSize() ++{ ++ if(bufferBitmap) { ++ return bufferBitmap->BitsLength()/4; ++ } ++ return 0; ++} ++ ++int ++FBView::Width() ++{ ++ return buffer_width; ++} ++ ++int ++FBView::Height() ++{ ++ return buffer_height; ++} ++ ++ +diff --git a/client/Haiku/MainView.h b/client/Haiku/MainView.h +new file mode 100644 +index 0000000..8597c85 +--- /dev/null ++++ b/client/Haiku/MainView.h +@@ -0,0 +1,74 @@ ++#ifndef _H_FBVIEW_ ++#define _H_FBVIEW_ ++ ++#include ++#include ++#include ++ ++extern "C" { ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++} ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++class FBView : public BView ++{ ++ public: ++ FBView(BRect rect); ++ FBView(BRect rect, int width, int height); ++ ~FBView(); ++ ++ void Paint(); ++ void SetInstance(freerdp *instance); ++ uint32 *GetBuffer(); ++ uint32 GetBufferSize(); ++ BPoint GetLastMousePos(); ++ ++ int Width(); ++ int Height(); ++protected: ++ virtual void Draw(BRect r); ++ virtual void MouseDown(BPoint p); ++ virtual void MouseUp(BPoint p); ++ virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message); ++ virtual void Pulse(void); ++ virtual void AttachedToWindow(void); ++ ++private: ++ int buffer_width; ++ int buffer_height; ++ BView *bufferView; ++ BBitmap *bufferBitmap; ++ BBitmap *closeBitmap; ++ BBitmap *zoomBitmap; ++ uint32 lastButtons; ++ BPoint lastPos; ++ freerdp *instance; ++ uint32 tickCount; ++ bool showBar; ++ BFont font; ++ BString title; ++}; ++ ++#endif +diff --git a/client/Haiku/MainWindow.cpp b/client/Haiku/MainWindow.cpp +new file mode 100644 +index 0000000..7718e3b +--- /dev/null ++++ b/client/Haiku/MainWindow.cpp +@@ -0,0 +1,129 @@ ++#include ++#include ++#include "MainWindow.h" ++ ++static const uint32 be_to_scan_codes[] = { ++VK_ESCAPE, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, ++VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12, VK_PRINT, VK_SCROLL, 0, ++////////// ++VK_OEM_3,VK_KEY_1,VK_KEY_2,VK_KEY_3,VK_KEY_4,VK_KEY_5,VK_KEY_6,VK_KEY_7,VK_KEY_8, ++VK_KEY_9,VK_KEY_0,VK_OEM_MINUS,VK_OEM_PLUS,VK_BACK,VK_INSERT|KBDEXT, ++VK_HOME|KBDEXT,VK_PRIOR|KBDEXT,VK_NUMLOCK,VK_DIVIDE|KBDEXT,VK_MULTIPLY,VK_SUBTRACT, ++////////// ++VK_TAB,VK_KEY_Q,VK_KEY_W,VK_KEY_E,VK_KEY_R,VK_KEY_T,VK_KEY_Y,VK_KEY_U,VK_KEY_I, ++VK_KEY_O,VK_KEY_P,VK_OEM_4,VK_OEM_6,VK_OEM_5,VK_DELETE|KBDEXT,VK_END|KBDEXT,VK_NEXT|KBDEXT, ++VK_NUMPAD7,VK_NUMPAD8,VK_NUMPAD9,VK_ADD, ++////////// ++VK_CAPITAL,VK_KEY_A,VK_KEY_S,VK_KEY_D,VK_KEY_F,VK_KEY_G,VK_KEY_H,VK_KEY_J,VK_KEY_K,VK_KEY_L,VK_OEM_1,VK_OEM_7,VK_RETURN, ++VK_NUMPAD4,VK_NUMPAD5,VK_NUMPAD6, ++////////// ++VK_LSHIFT,VK_KEY_Z,VK_KEY_X,VK_KEY_C,VK_KEY_V,VK_KEY_B,VK_KEY_N,VK_KEY_M,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2, ++VK_RSHIFT,VK_UP|KBDEXT,VK_NUMPAD1,VK_NUMPAD2,VK_NUMPAD3,VK_RETURN|KBDEXT, ++////////// ++VK_LCONTROL,VK_LMENU,VK_SPACE,VK_RMENU,VK_RCONTROL,VK_LEFT|KBDEXT,VK_DOWN|KBDEXT,VK_RIGHT|KBDEXT,VK_NUMPAD0,VK_DECIMAL, ++VK_LWIN|KBDEXT,VK_RWIN|KBDEXT,127,0 ++}; ++ ++ ++MainWindow::MainWindow(BRect frame, const char* title, freerdp *inst) ++ : BWindow(frame, title, B_TITLED_WINDOW_LOOK,B_NORMAL_WINDOW_FEEL,B_NOT_RESIZABLE|B_NOT_ZOOMABLE) ++{ ++ fb = new FBView(Bounds(), frame.Width(),frame.Height()); ++ AddChild(fb); ++ SetInstance(inst); ++ SetPulseRate(100000); ++} ++ ++ ++MainWindow::~MainWindow() ++{ ++} ++ ++void ++MainWindow::MessageReceived(BMessage *message) ++{ ++ switch(message->what) { ++ case B_UNMAPPED_KEY_DOWN: ++ case B_KEY_DOWN: ++ { ++ rdpInput* input = instance->input; ++ ++ const char *bytes; ++ int32 key; ++ uint32 keycode; ++ message->FindInt32("key",&key); ++ keycode = be_to_scan_codes[(uint8)key-1]; ++ DWORD scancode = GetVirtualScanCodeFromVirtualKeyCode(keycode, input->context->settings->KeyboardType); ++ ++ if (scancode) ++ freerdp_input_send_keyboard_event_ex(input, TRUE, scancode); ++ ++ break; ++ } ++ case B_UNMAPPED_KEY_UP: ++ case B_KEY_UP: ++ { ++ rdpInput* input = instance->input; ++ ++ const char *bytes; ++ int32 key; ++ uint32 keycode; ++ message->FindInt32("key",&key); ++ keycode = be_to_scan_codes[(uint8)key-1]; ++ DWORD scancode = GetVirtualScanCodeFromVirtualKeyCode(keycode, input->context->settings->KeyboardType); ++ ++ if (scancode) ++ freerdp_input_send_keyboard_event_ex(input, FALSE, scancode); ++ ++ break; ++ } ++ case B_MOUSE_WHEEL_CHANGED: ++ { ++ rdpInput* input = instance->input; ++ BPoint pos = fb->GetLastMousePos(); ++ ++ float shift=0; ++ UINT16 flags; ++ ++ flags = PTR_FLAGS_WHEEL; ++ ++ if(message->FindFloat("be:wheel_delta_y",&shift)==B_OK) { ++ if(shift<0) { ++ flags |= 0x0078; ++ } ++ if(shift>0) { ++ flags |= PTR_FLAGS_WHEEL_NEGATIVE | 0x0088; ++ } ++ } ++ input->MouseEvent(input, flags, pos.x, pos.y); ++ break; ++ } ++// case B_CLIPBOARD_CHANGED: ++// { ++// break; ++// } ++ case B_WINDOW_ACTIVATED: ++ case B_WORKSPACE_ACTIVATED: ++ { ++ break; ++ } ++ default: ++ BWindow::MessageReceived(message); ++ break; ++ } ++} ++ ++ ++void ++MainWindow::SetInstance(freerdp *inst) ++{ ++ instance = inst; ++ fb->SetInstance(inst); ++} ++ ++bool ++MainWindow::QuitRequested() ++{ ++ freerdp_abort_connect(instance); ++ return false; ++} +diff --git a/client/Haiku/MainWindow.h b/client/Haiku/MainWindow.h +new file mode 100644 +index 0000000..709a2c0 +--- /dev/null ++++ b/client/Haiku/MainWindow.h +@@ -0,0 +1,50 @@ ++#ifndef _TEST_WINDOW_H ++#define _TEST_WINDOW_H ++ ++#include ++#include ++#include ++ ++extern "C" { ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++} ++ ++#include ++#include ++#include ++#include ++ ++#include "MainView.h" ++ ++class MainWindow : public BWindow { ++ public: ++ MainWindow(BRect rect, const char* name, freerdp *instance); ++ virtual ~MainWindow(); ++ ++ virtual void MessageReceived(BMessage *message); ++ bool QuitRequested(); ++ ++ void SetInstance(freerdp *instance); ++ ++ FBView *fb; ++ private: ++ freerdp *instance; ++}; ++ ++#endif ++ ++ +diff --git a/client/Haiku/ModuleOptions.cmake b/client/Haiku/ModuleOptions.cmake +new file mode 100644 +index 0000000..130bfa1 +--- /dev/null ++++ b/client/Haiku/ModuleOptions.cmake +@@ -0,0 +1,4 @@ ++ ++set(FREERDP_CLIENT_NAME "hfreerdp") ++set(FREERDP_CLIENT_PLATFORM "Haiku") ++set(FREERDP_CLIENT_VENDOR "FreeRDP") +diff --git a/client/Haiku/Resources.rdef b/client/Haiku/Resources.rdef +new file mode 100644 +index 0000000..86dd149 +--- /dev/null ++++ b/client/Haiku/Resources.rdef +@@ -0,0 +1,55 @@ ++/* ++ * Resources.rdef ++ */ ++ ++resource app_signature "application/x-vnd.freerdp"; ++ ++resource app_flags B_MULTIPLE_LAUNCH; ++ ++resource app_version { ++ major = 2, ++ middle = 0, ++ minor = 0, ++ variety = 0, ++ internal = 0, ++ short_info = "FreeRDP", ++ long_info = "FreeRDP" ++}; ++ ++resource(1, "close.png") #'PNG ' import "images/close.png"; ++resource(2, "zoom.png") #'PNG ' import "images/zoom.png"; ++ ++resource vector_icon { ++ $"6E6369662803010000020016023CC7ED389BBFBA16553E39AF4977C842ADC700" ++ $"FFFFD3020016023C529D3753A2B8966F3D9D074B6044496AAF0047FFA0020016" ++ $"02BC4E75BC411A3C90D9BCA00C47587D4ABA850090FFD40200160238313C3B5C" ++ $"F0BFCD953C7AAB4C13943FCAF901ECFFC3054B04016B020006033E2F99387F17" ++ $"BA42DC3FF5B84A0E32482C90001D1E2C3D454658FF010101020006023879063B" ++ $"8224BE2CC73B10D94A1F6F49B89400242222FF9A9A9A020006033C69A6000000" ++ $"0000003E186148800049800000D4CECE58F3F3F3FFD9D9D90389FF0005000500" ++ $"0500030100000500033A3A4303F46E0C03010000020016023CC7ED389BBFBA16" ++ $"553E39AF4977C842ADC700FFFFD3020006023C529D3753A2B8966F3D9D074B60" ++ $"44496AAF00474747FFA5A0A002001602BC4E75BC411A3C90D9BCA00C47587D4A" ++ $"BA850090FFD40200160238313C3B5CF0BFCD953C7AAB4C13943FCAF901ECFFC3" ++ $"054B04017E020006033E2F99387F17BA42DC3FF5B84A0E32482C90001D1E2C3D" ++ $"454658FF010101020006023879063B8224BE2CC73B10D94A1F6F49B894002422" ++ $"22FF9A9A9A020006033C69A60000000000003E186148800049800000D4CECE58" ++ $"F3F3F3FFD9D9D9038AFF01050005000500030100000500033A3A43033A3A4303" ++ $"0100000300AAFF0300FF0003FFAA002200000A062228224A485E525252302C22" ++ $"0A042228483852302C220A044838485E525252300A042228224A485E48380A04" ++ $"453A45572446242C0A04B560442446242CB569B8200A0445544557244626440A" ++ $"0438263D234E28492C080438263D234E284E2E0A03492C4E284E2E0802425843" ++ $"C9830A06486054606052CA1BC5B95C4D524800000A0430303050505050300000" ++ $"000000000A04B570B86DB57DC12FC17CC729C17CBD8F0A062228224A485E5252" ++ $"52302C220A062228224A485E525252302C220A042228483852302C220A044838" ++ $"485E525252300A042228224A485E48380A042228224A485E48380A04453A4557" ++ $"2446242C0A04B560442446242CB569B8200A04B560442446242CB569B8200A04" ++ $"45544557244626440A0438263D234E28492C080438263D234E284E2E0A03492C" ++ $"4E284E2E0802425843C9830A04B570B86DB57DC12FC17CC729C17CBD8F0F0A06" ++ $"010C000A0001091001178400040A040108000A05010A000A0001011001178400" ++ $"040A010102000A03020405000A080106000A090107000A0A010B1815FF011782" ++ $"20040A020103000A110112023E00150000000000003E260D42CEDE44B6B40A26" ++ $"0112023E21EE0000000000003E260D483363471B5A0A250112023E0015000000" ++ $"0000003E260D42CEDE482DAD0A270112023E21EE0000000000003E41BA483363" ++ $"4901E6" ++}; +diff --git a/client/Haiku/images/close.png b/client/Haiku/images/close.png +new file mode 100644 +index 0000000000000000000000000000000000000000..584d790923a6dc766842e4baa2227f64aadf8d69 +GIT binary patch +literal 163 +zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|S)MMAAsQ2>UNYosP~dTU*#5C1 +z?&xHT3L*8qvmS)Rs^%_~Q2YFsS$|1!`it3(se32J%wO~`Wl79&Ge*bW6bJsAT;5qx +zyj8p8Sia;7Tn;+p{rlF+C$9^_wg>sMZ`w6A=~t~(WG?TLi_iZua`!Yw+VW_I0BvRP +MboFyt=akR{0P0ga`Tzg` + +literal 0 +HcmV?d00001 + +diff --git a/client/Haiku/images/zoom.png b/client/Haiku/images/zoom.png +new file mode 100644 +index 0000000000000000000000000000000000000000..4b796324247432ac5b5a9c595daf97cf4c2e33ab +GIT binary patch +literal 248 +zcmVT~(m?k3lv=wGL_)S2bcK<~Zn+v+O$JD4w +z=7(6M7ckPgvJNZ)kve)5dRo^uk}R^|9$dN;gjfxLl)0OGO6v+p-R=0fr%y=TVb9RL +y(0D?Gwt^cePMdcmQk({o_O=|SbiIe?JE