[client,sdl] add user dialogs

This commit is contained in:
akallabeth 2023-02-14 16:16:24 +01:00 committed by akallabeth
parent a0d38914d6
commit 97415f0d52
31 changed files with 35595 additions and 20 deletions

View File

@ -45,12 +45,13 @@ if (WITH_DEBUG_SDL_KBD_EVENTS)
add_definitions(-DWITH_DEBUG_SDL_KBD_EVENTS)
endif()
find_package(SDL2 REQUIRED)
find_package(SDL2 REQUIRED COMPONENTS)
include_directories(${SDL2_INCLUDE_DIR})
include_directories(${SDL2_INCLUDE_DIRS})
find_package(Threads REQUIRED)
add_subdirectory(dialogs)
set(SRCS
sdl_types.hpp
sdl_utils.cpp
@ -76,6 +77,7 @@ set(LIBS
freerdp
freerdp-client
Threads::Threads
dialogs
)
option(WITH_WEBVIEW "Build with QtWebEngine support for AAD login popup browser" OFF)

View File

@ -0,0 +1,34 @@
find_package(SDL2TTF REQUIRED COMPONENTS)
include_directories(${SDL2TTF_INCLUDE_DIR})
set(SRCS
sdl_button.hpp
sdl_button.cpp
sdl_buttons.hpp
sdl_buttons.cpp
sdl_dialogs.cpp
sdl_dialogs.hpp
sdl_widget.hpp
sdl_widget.cpp
sdl_input.hpp
sdl_input.cpp
sdl_input_widgets.cpp
sdl_input_widgets.hpp
sdl_select.hpp
sdl_select.cpp
sdl_selectlist.hpp
sdl_selectlist.cpp
)
set(LIBS
${SDL2TTF_LIBRARY}
font
)
add_subdirectory(font)
add_library(dialogs STATIC
${SRCS}
)
target_link_libraries(dialogs PRIVATE ${LIBS})

View File

@ -0,0 +1,13 @@
add_executable(
convert_font_to_c
convert_font_to_c.cpp
)
add_library(font OBJECT
opensans_variable_font.hpp
opensans_variable_font.cpp
font_writer.hpp
font_writer.cpp
)
set_property(TARGET font PROPERTY POSITION_INDEPENDENT_CODE ON)

View File

@ -0,0 +1,2 @@
all:
cc -o convert_font_to_c convert_font_to_c.c

View File

@ -0,0 +1,103 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include "opensans_variable_font.hpp"
#define BLOCK_SIZE 8192
#define LINEWIDTH 80
static void usage(const char* prg)
{
fprintf(stderr, "%s <font file> <buffer file>\n", prg);
}
static int write_header(FILE* out, const char* font, size_t size)
{
fprintf(out, "/* AUTOGENERATED file, do not edit\n");
fprintf(out, " *\n");
fprintf(out, " * contains the converted font %s\n", font);
fprintf(out, " */\n");
fprintf(out, "\n#pragma once\n");
fprintf(out, "#include <vector>\n");
fprintf(out, "\n");
fprintf(out, "const std::vector<unsigned char> font_buffer ={\n");
return 0;
}
static int read(FILE* out, const char* font)
{
FILE* fp = fopen(font, "rb");
if (!fp)
{
fprintf(stderr, "Failed to open font file '%s'\n", font);
return -11;
}
fseek(fp, 0, SEEK_END);
off_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
const int rc = write_header(out, font, (size_t)size);
if (rc != 0)
{
fclose(fp);
return -12;
}
int first = 1;
while (!feof(fp))
{
char buffer[BLOCK_SIZE] = {};
const size_t read = fread(buffer, 1, sizeof(buffer), fp);
for (size_t x = 0; x < read; x++)
{
if (!first)
fprintf(out, ",");
first = 0;
fprintf(out, "0x%02" PRIx8, buffer[x] & 0xFF);
if ((x % (LINEWIDTH / 5)) == 0)
fprintf(out, "\n");
}
}
fclose(fp);
return rc;
}
static int write_trailer(FILE* out)
{
fprintf(out, "};\n");
return 0;
}
int main(int argc, char* argv[])
{
int rc = -3;
if (argc != 3)
{
usage(argv[0]);
return -1;
}
const char* font = argv[1];
const char* header = argv[2];
FILE* fp = fopen(header, "w");
if (!fp)
{
fprintf(stderr, "Failed to open header file '%s'", header);
return -2;
}
rc = read(fp, font);
if (rc != 0)
goto fail;
rc = write_trailer(fp);
fail:
fclose(fp);
return rc;
}

View File

@ -0,0 +1,22 @@
#include <filesystem>
#include <fstream>
#include "font_writer.hpp"
#include "opensans_variable_font.hpp"
std::string create_and_return_temorary_font()
{
static std::string name;
if (name.empty())
{
auto path = std::filesystem::temp_directory_path();
path /= "font.ttf";
std::ofstream ofs(path, std::ios::binary);
ofs.write(reinterpret_cast<const char*>(font_buffer.data()), font_buffer.size());
name = path.u8string();
}
return name;
}

View File

@ -0,0 +1,5 @@
#pragma once
#include <string>
std::string create_and_return_temorary_font();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
#pragma once
#include <vector>
extern const std::vector<unsigned char> font_buffer;

View File

@ -0,0 +1,66 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client Channels
*
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <assert.h>
#include "sdl_button.hpp"
static const SDL_Color buttonbackgroundcolor = { 0x69, 0x66, 0x63, 0xff };
static const SDL_Color buttonhighlightcolor = { 0xcd, 0xca, 0x35, 0x60 };
static const SDL_Color buttonfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
SdlButton::SdlButton(SDL_Renderer* renderer, const std::string& label, int id, const SDL_Rect& rect)
: SdlWidget(renderer, rect, false), _name(label), _id(id)
{
assert(renderer);
update_text(renderer, _name, buttonfontcolor, buttonbackgroundcolor);
}
SdlButton::SdlButton(SdlButton&& other) noexcept
: SdlWidget(std::move(other)), _name(std::move(other._name)), _id(std::move(other._id))
{
}
SdlButton::~SdlButton()
{
}
bool SdlButton::highlight(SDL_Renderer* renderer)
{
assert(renderer);
std::vector<SDL_Color> colors = { buttonbackgroundcolor, buttonhighlightcolor };
if (!fill(renderer, colors))
return false;
return update_text(renderer, _name, buttonfontcolor);
}
bool SdlButton::update(SDL_Renderer* renderer)
{
assert(renderer);
return update_text(renderer, _name, buttonfontcolor, buttonbackgroundcolor);
}
int SdlButton::id() const
{
return _id;
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <string>
#include "sdl_widget.hpp"
class SdlButton : public SdlWidget
{
public:
SdlButton(SDL_Renderer* renderer, const std::string& label, int id, const SDL_Rect& rect);
SdlButton(SdlButton&& other) noexcept;
virtual ~SdlButton();
bool highlight(SDL_Renderer* renderer);
bool update(SDL_Renderer* renderer);
int id() const;
private:
SdlButton(const SdlButton& other) = delete;
private:
std::string _name;
int _id;
};

View File

@ -0,0 +1,64 @@
#include <assert.h>
#include "sdl_buttons.hpp"
static const Uint32 vpadding = 5;
static const Uint32 hpadding = 10;
SdlButtonList::SdlButtonList()
{
}
bool SdlButtonList::populate(SDL_Renderer* renderer, const std::vector<std::string>& labels,
const std::vector<int>& ids, Sint32 offsetY, Sint32 width,
Sint32 height)
{
assert(renderer);
assert(width >= 0);
assert(height >= 0);
assert(labels.size() == ids.size());
_list.clear();
for (size_t x = 0; x < ids.size(); x++)
{
const size_t offsetX = x * (static_cast<size_t>(width) + hpadding);
const SDL_Rect rect = { static_cast<int>(offsetX), offsetY, width, height };
_list.push_back({ renderer, labels[x], ids[x], rect });
}
return true;
}
SdlButtonList::~SdlButtonList()
{
}
SdlButton* SdlButtonList::get_selected(const SDL_MouseButtonEvent& button)
{
const Sint32 x = button.x;
const Sint32 y = button.y;
for (auto& btn : _list)
{
auto r = btn.rect();
if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
return &btn;
}
return nullptr;
}
void SdlButtonList::clear()
{
_list.clear();
}
bool SdlButtonList::update(SDL_Renderer* renderer)
{
assert(renderer);
for (auto& btn : _list)
{
if (!btn.update(renderer))
return false;
}
return true;
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <vector>
#include "sdl_button.hpp"
class SdlButtonList
{
public:
SdlButtonList();
virtual ~SdlButtonList();
bool populate(SDL_Renderer* renderer, const std::vector<std::string>& labels,
const std::vector<int>& ids, Sint32 offsetY, Sint32 width, Sint32 heigth);
bool update(SDL_Renderer* renderer);
SdlButton* get_selected(const SDL_MouseButtonEvent& button);
void clear();
private:
SdlButtonList(const SdlButtonList& other) = delete;
SdlButtonList(SdlButtonList&& other) = delete;
private:
std::vector<SdlButton> _list;
};

View File

@ -0,0 +1,527 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#include <vector>
#include <string>
#include <freerdp/log.h>
#include <freerdp/utils/smartcardlogon.h>
#include <SDL.h>
#include "sdl_dialogs.hpp"
#include "sdl_input.hpp"
#include "sdl_input_widgets.hpp"
#include "sdl_select.hpp"
#include "sdl_selectlist.hpp"
#define TAG CLIENT_TAG("SDL.dialogs")
enum
{
SHOW_DIALOG_ACCEPT_REJECT = 1,
SHOW_DIALOG_TIMED_ACCEPT = 2
};
static const char* type_str_for_flags(UINT32 flags)
{
const char* type = "RDP-Server";
if (flags & VERIFY_CERT_FLAG_GATEWAY)
type = "RDP-Gateway";
if (flags & VERIFY_CERT_FLAG_REDIRECT)
type = "RDP-Redirect";
return type;
}
static int sdl_show_dialog(rdpContext* context, const char* title, const char* message,
Sint32 flags)
{
if (!sdl_push_user_event(SDL_USEREVENT_SHOW_DIALOG, title, message, flags))
return 0;
while (!freerdp_shall_disconnect_context(context))
{
SDL_Event event = { 0 };
const int rc = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_USEREVENT_SHOW_RESULT,
SDL_USEREVENT_SHOW_RESULT);
if (rc > 0)
return event.user.code;
Sleep(1);
}
return 0;
}
BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
rdp_auth_reason reason)
{
BOOL res = FALSE;
const char* target = freerdp_settings_get_server_name(instance->context->settings);
switch (reason)
{
case GW_AUTH_HTTP:
case GW_AUTH_RDG:
case GW_AUTH_RPC:
target =
freerdp_settings_get_string(instance->context->settings, FreeRDP_GatewayHostname);
break;
default:
break;
}
char* title = nullptr;
size_t titlesize = 0;
winpr_asprintf(&title, &titlesize, "Credentials required for %s", target);
char* u = nullptr;
char* d = nullptr;
char* p = nullptr;
if (username)
u = *username;
if (domain)
d = *domain;
if (password)
p = *password;
if (!sdl_push_user_event(SDL_USEREVENT_AUTH_DIALOG, title, u, d, p, reason))
goto fail;
while (!freerdp_shall_disconnect_context(instance->context))
{
SDL_Event event = { 0 };
const int rc = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_USEREVENT_AUTH_RESULT,
SDL_USEREVENT_AUTH_RESULT);
if (rc > 0)
{
SDL_UserAuthArg* arg = (SDL_UserAuthArg*)event.padding;
res = arg->result != 0 ? TRUE : FALSE;
free(*username);
free(*domain);
free(*password);
*username = arg->user;
*domain = arg->domain;
*password = arg->password;
break;
}
Sleep(1);
}
fail:
free(title);
return res;
}
BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
DWORD* choice, BOOL gateway)
{
BOOL res = FALSE;
std::vector<std::string> strlist;
std::vector<const char*> list;
for (DWORD i = 0; i < count; i++)
{
const SmartcardCertInfo* cert = cert_list[i];
char* reader = ConvertWCharToUtf8Alloc(cert->reader, nullptr);
char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, nullptr);
char* msg = nullptr;
size_t len = 0;
winpr_asprintf(&msg, &len,
"%s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s",
container_name, reader, cert->userHint, cert->domainHint, cert->subject,
cert->issuer, cert->upn);
strlist.push_back(msg);
free(msg);
free(reader);
free(container_name);
auto m = strlist.back();
list.push_back(m.c_str());
}
const char* title = "Select a logon smartcard certificate";
if (gateway)
title = "Select a gateway logon smartcard certificate";
if (!sdl_push_user_event(SDL_USEREVENT_SCARD_DIALOG, title, list.data(), count))
goto fail;
while (!freerdp_shall_disconnect_context(instance->context))
{
SDL_Event event = { 0 };
const int rc = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_USEREVENT_SCARD_RESULT,
SDL_USEREVENT_SCARD_RESULT);
if (rc > 0)
{
res = TRUE;
*choice = (DWORD)event.user.code;
break;
}
Sleep(1);
}
fail:
return res;
}
BOOL sdl_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
BOOL isConsentMandatory, size_t length, const WCHAR* wmessage)
{
if (!isDisplayMandatory)
return TRUE;
char* title = nullptr;
size_t len = 0;
winpr_asprintf(&title, &len, "[gateway]");
Sint32 flags = 0;
if (isConsentMandatory)
flags = SHOW_DIALOG_ACCEPT_REJECT;
else if (isDisplayMandatory)
flags = SHOW_DIALOG_TIMED_ACCEPT;
char* message = ConvertWCharNToUtf8Alloc(wmessage, length, nullptr);
const int rc = sdl_show_dialog(instance->context, title, message, flags);
free(title);
free(message);
return rc > 0 ? TRUE : FALSE;
}
int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
{
int rc = -1;
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;
char* title = nullptr;
size_t tlen = 0;
winpr_asprintf(&title, &tlen, "[%s] info",
freerdp_settings_get_server_name(instance->context->settings));
char* message = nullptr;
size_t mlen = 0;
winpr_asprintf(&message, &mlen, "Logon Error Info %s [%s]", str_data, str_type);
rc = sdl_show_dialog(instance->context, title, message, SHOW_DIALOG_ACCEPT_REJECT);
free(title);
free(message);
return rc;
}
static DWORD sdl_show_ceritifcate_dialog(rdpContext* context, const char* title,
const char* message)
{
if (!sdl_push_user_event(SDL_USEREVENT_CERT_DIALOG, title, message))
return 0;
while (!freerdp_shall_disconnect_context(context))
{
SDL_Event event = { 0 };
const int rc = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_USEREVENT_CERT_RESULT,
SDL_USEREVENT_CERT_RESULT);
if (rc > 0)
return (DWORD)event.user.code;
Sleep(1);
}
return 0;
}
DWORD sdl_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
const char* common_name, const char* subject,
const char* issuer, const char* new_fingerprint,
const char* old_subject, const char* old_issuer,
const char* old_fingerprint, DWORD flags)
{
const char* type = type_str_for_flags(flags);
WINPR_ASSERT(instance);
WINPR_ASSERT(instance->context);
WINPR_ASSERT(instance->context->settings);
/* Newer versions of FreeRDP allow exposing the whole PEM by setting
* FreeRDP_CertificateCallbackPreferPEM to TRUE
*/
char* new_fp_str = nullptr;
size_t len = 0;
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
{
winpr_asprintf(&new_fp_str, &len,
"----------- Certificate --------------\n"
"%s\n"
"--------------------------------------\n",
new_fingerprint);
}
else
winpr_asprintf(&new_fp_str, &len, "Thumbprint: %s\n", new_fingerprint);
/* Newer versions of FreeRDP allow exposing the whole PEM by setting
* FreeRDP_CertificateCallbackPreferPEM to TRUE
*/
char* old_fp_str = nullptr;
size_t olen = 0;
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
{
winpr_asprintf(&old_fp_str, &olen,
"----------- Certificate --------------\n"
"%s\n"
"--------------------------------------\n",
old_fingerprint);
}
else
winpr_asprintf(&old_fp_str, &olen, "Thumbprint: %s\n", old_fingerprint);
const char* collission_str = "";
if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
{
collission_str =
"A matching entry with legacy SHA1 was found in local known_hosts2 store.\n"
"If you just upgraded from a FreeRDP version before 2.0 this is expected.\n"
"The hashing algorithm has been upgraded from SHA1 to SHA256.\n"
"All manually accepted certificates must be reconfirmed!\n"
"\n";
}
char* title = nullptr;
size_t tlen = 0;
winpr_asprintf(&title, &tlen, "Certificate for %s:%" PRIu16 " (%s) has changed", host, port,
type);
char* message = nullptr;
size_t mlen = 0;
winpr_asprintf(&message, &mlen,
"New Certificate details:\n"
"Common Name: %s\n"
"Subject: %s\n"
"Issuer: %s\n"
"%s\n"
"Old Certificate details:\n"
"Subject: %s\n"
"Issuer: %s\n"
"%s\n"
"%s\n"
"The above X.509 certificate does not match the certificate used for previous "
"connections.\n"
"This may indicate that the certificate has been tampered with.\n"
"Please contact the administrator of the RDP server and clarify.\n",
common_name, subject, issuer, new_fp_str, old_subject, old_issuer, old_fp_str,
collission_str);
const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
free(title);
free(message);
free(new_fp_str);
free(old_fp_str);
return rc;
}
DWORD sdl_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
const char* common_name, const char* subject, const char* issuer,
const char* fingerprint, DWORD flags)
{
const char* type = type_str_for_flags(flags);
/* Newer versions of FreeRDP allow exposing the whole PEM by setting
* FreeRDP_CertificateCallbackPreferPEM to TRUE
*/
char* fp_str = nullptr;
size_t len = 0;
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
{
winpr_asprintf(&fp_str, &len,
"----------- Certificate --------------\n"
"%s\n"
"--------------------------------------\n",
fingerprint);
}
else
winpr_asprintf(&fp_str, &len, "Thumbprint: %s\n", fingerprint);
char* title = nullptr;
size_t tlen = 0;
winpr_asprintf(&title, &tlen, "New certificate for %s:%" PRIu16 " (%s)", host, port, type);
char* message = nullptr;
size_t mlen = 0;
winpr_asprintf(
&message, &mlen,
"Common Name: %s\n"
"Subject: %s\n"
"Issuer: %s\n"
"%s\n"
"The above X.509 certificate could not be verified, possibly because you do not have\n"
"the CA certificate in your certificate store, or the certificate has expired.\n"
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n",
common_name, subject, issuer, fp_str);
const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
free(fp_str);
free(title);
free(message);
return rc;
}
BOOL sdl_cert_dialog_show(const char* title, const char* message)
{
int buttonid = -1;
enum
{
BUTTONID_CERT_ACCEPT_PERMANENT = 23,
BUTTONID_CERT_ACCEPT_TEMPORARY = 24,
BUTTONID_CERT_DENY = 25
};
const SDL_MessageBoxButtonData buttons[] = {
{ 0, BUTTONID_CERT_ACCEPT_PERMANENT, "permanent" },
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_CERT_ACCEPT_TEMPORARY, "temporary" },
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_CERT_DENY, "cancel" }
};
const SDL_MessageBoxData data = { SDL_MESSAGEBOX_WARNING, nullptr, title, message,
ARRAYSIZE(buttons), buttons, nullptr };
const int rc = SDL_ShowMessageBox(&data, &buttonid);
Sint32 value = -1;
if (rc < 0)
value = 0;
else
{
switch (buttonid)
{
case BUTTONID_CERT_ACCEPT_PERMANENT:
value = 1;
break;
case BUTTONID_CERT_ACCEPT_TEMPORARY:
value = 2;
break;
default:
value = 0;
break;
}
}
return sdl_push_user_event(SDL_USEREVENT_CERT_RESULT, value);
}
BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags)
{
int buttonid = -1;
enum
{
BUTTONID_SHOW_ACCEPT = 24,
BUTTONID_SHOW_DENY = 25
};
const SDL_MessageBoxButtonData buttons[] = {
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_SHOW_ACCEPT, "accept" },
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_SHOW_DENY, "cancel" }
};
const int button_cnt = (flags & SHOW_DIALOG_ACCEPT_REJECT) ? 2 : 1;
const SDL_MessageBoxData data = {
SDL_MESSAGEBOX_WARNING, nullptr, title, message, button_cnt, buttons, nullptr
};
const int rc = SDL_ShowMessageBox(&data, &buttonid);
Sint32 value = -1;
if (rc < 0)
value = 0;
else
{
switch (buttonid)
{
case BUTTONID_SHOW_ACCEPT:
value = 1;
break;
default:
value = 0;
break;
}
}
return sdl_push_user_event(SDL_USEREVENT_SHOW_RESULT, value);
}
BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args)
{
const std::vector<std::string> auth = { "Username: ", "Domain: ",
"Password: " };
const std::vector<std::string> authPin = { "Device: ", "PIN: " };
const std::vector<std::string> gw = { "GatewayUsername: ", "GatewayDomain: ",
"GatewayPassword: " };
std::vector<std::string> prompt;
Sint32 rc = -1;
switch (args->result)
{
case AUTH_SMARTCARD_PIN:
prompt = authPin;
break;
case AUTH_TLS:
case AUTH_RDP:
case AUTH_NLA:
prompt = auth;
break;
case GW_AUTH_HTTP:
case GW_AUTH_RDG:
case GW_AUTH_RPC:
prompt = gw;
break;
default:
break;
}
std::vector<std::string> result;
if (!prompt.empty())
{
std::vector<std::string> initial{ args->user ? args->user : "Smartcard", "" };
std::vector<Uint32> flags = { SdlInputWidget::SDL_INPUT_READONLY,
SdlInputWidget::SDL_INPUT_MASK };
if (args->result != AUTH_SMARTCARD_PIN)
{
initial = { args->user ? args->user : "", args->domain ? args->domain : "",
args->password ? args->password : "" };
flags = { 0, 0, SdlInputWidget::SDL_INPUT_MASK };
}
SdlInputWidgetList ilist(args->title, prompt, initial, flags);
rc = ilist.run(result);
}
if ((rc <= 0) || (result.size() < prompt.size()))
return FALSE;
return sdl_push_user_event(SDL_USEREVENT_AUTH_RESULT, _strdup(result[0].c_str()),
_strdup(result[1].c_str()), _strdup(result[2].c_str()), rc);
}
BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list)
{
std::vector<std::string> vlist;
for (Sint32 x = 0; x < count; x++)
vlist.push_back(list[x]);
SdlSelectList slist(title, vlist);
Sint32 value = slist.run();
return sdl_push_user_event(SDL_USEREVENT_SCARD_RESULT, value);
}

View File

@ -0,0 +1,51 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#pragma once
#include <winpr/wtypes.h>
#include <freerdp/freerdp.h>
#include "../sdl_types.hpp"
#include "../sdl_utils.hpp"
BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
rdp_auth_reason reason);
BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
DWORD* choice, BOOL gateway);
DWORD sdl_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
const char* common_name, const char* subject, const char* issuer,
const char* fingerprint, DWORD flags);
DWORD sdl_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
const char* common_name, const char* subject,
const char* issuer, const char* new_fingerprint,
const char* old_subject, const char* old_issuer,
const char* old_fingerprint, DWORD flags);
int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type);
BOOL sdl_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
BOOL isConsentMandatory, size_t length, const WCHAR* message);
BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags);
BOOL sdl_cert_dialog_show(const char* title, const char* message);
BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list);
BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args);

View File

@ -0,0 +1,164 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#include "sdl_input.hpp"
#include <assert.h>
#include <stdbool.h>
#include <string>
#include <SDL.h>
#include <SDL_ttf.h>
#include "sdl_widget.hpp"
#include "sdl_button.hpp"
#include "sdl_buttons.hpp"
static const SDL_Color inputbackgroundcolor = { 0x56, 0x56, 0x56, 0xff };
static const SDL_Color inputhighlightcolor = { 0x80, 0, 0, 0x60 };
static const SDL_Color inputmouseovercolor = { 0, 0x80, 0, 0x60 };
static const SDL_Color inputfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
static const SDL_Color labelbackgroundcolor = { 0x56, 0x56, 0x56, 0xff };
static const SDL_Color labelfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
static const Uint32 vpadding = 5;
static const Uint32 hpadding = 10;
SdlInputWidget::SdlInputWidget(SDL_Renderer* renderer, const std::string& label,
const std::string& initial, Uint32 flags, size_t offset,
size_t width, size_t height)
: _flags(flags), _text(initial), _text_label(label),
_label(renderer,
{ 0, static_cast<int>(offset * (height + vpadding)), static_cast<int>(width),
static_cast<int>(height) },
false),
_input(renderer,
{ static_cast<int>(width + hpadding), static_cast<int>(offset * (height + vpadding)),
static_cast<int>(width), static_cast<int>(height) },
true),
_mouseover(false), _highlight(false)
{
}
SdlInputWidget::SdlInputWidget(SdlInputWidget&& other) noexcept
: _flags(std::move(other._flags)), _text(std::move(other._text)),
_text_label(std::move(other._text_label)), _label(std::move(other._label)),
_input(std::move(other._input)), _mouseover(other._mouseover), _highlight(other._highlight)
{
}
bool SdlInputWidget::fill_label(SDL_Renderer* renderer, SDL_Color color)
{
if (!_label.fill(renderer, color))
return false;
return _label.update_text(renderer, _text_label, labelfontcolor);
}
bool SdlInputWidget::update_label(SDL_Renderer* renderer)
{
return _label.update_text(renderer, _text_label, labelfontcolor, labelbackgroundcolor);
}
bool SdlInputWidget::set_mouseover(SDL_Renderer* renderer, bool mouseOver)
{
if (readonly())
return true;
_mouseover = mouseOver;
return update_input(renderer);
}
bool SdlInputWidget::set_highlight(SDL_Renderer* renderer, bool highlight)
{
if (readonly())
return true;
_highlight = highlight;
return update_input(renderer);
}
bool SdlInputWidget::update_input(SDL_Renderer* renderer)
{
std::vector<SDL_Color> colors = { inputbackgroundcolor };
if (_highlight)
colors.push_back(inputhighlightcolor);
if (_mouseover)
colors.push_back(inputmouseovercolor);
if (!_input.fill(renderer, colors))
return false;
return update_input(renderer, inputfontcolor);
}
bool SdlInputWidget::resize_input(size_t size)
{
_text.resize(size);
return true;
}
bool SdlInputWidget::remove_str(SDL_Renderer* renderer, size_t count)
{
if (readonly())
return true;
assert(renderer);
if (_text.empty())
return true;
if (!resize_input(_text.size() - count))
return false;
return update_input(renderer);
}
bool SdlInputWidget::append_str(SDL_Renderer* renderer, const std::string& str)
{
assert(renderer);
if (readonly())
return true;
_text.append(str);
if (!resize_input(_text.size()))
return false;
return update_input(renderer);
}
const SDL_Rect& SdlInputWidget::input_rect() const
{
return _input.rect();
}
std::string SdlInputWidget::value() const
{
return _text;
}
bool SdlInputWidget::readonly() const
{
return (_flags & SDL_INPUT_READONLY) != 0;
}
bool SdlInputWidget::update_input(SDL_Renderer* renderer, SDL_Color fgcolor)
{
std::string text = _text;
if (!text.empty())
{
if (_flags & SDL_INPUT_MASK)
memset(text.data(), '*', text.length());
}
return _input.update_text(renderer, text, fgcolor);
}

View File

@ -0,0 +1,72 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#pragma once
#include <vector>
#include <string>
#include <SDL.h>
#include "sdl_widget.hpp"
class SdlInputWidget
{
public:
enum
{
SDL_INPUT_MASK = 1,
SDL_INPUT_READONLY = 2
};
public:
SdlInputWidget(SDL_Renderer* renderer, const std::string& label, const std::string& initial,
Uint32 flags, size_t offset, size_t width, size_t height);
SdlInputWidget(SdlInputWidget&& other) noexcept;
bool fill_label(SDL_Renderer* renderer, SDL_Color color);
bool update_label(SDL_Renderer* renderer);
bool set_mouseover(SDL_Renderer* renderer, bool mouseOver);
bool set_highlight(SDL_Renderer* renderer, bool hightlight);
bool update_input(SDL_Renderer* renderer);
bool resize_input(size_t size);
bool remove_str(SDL_Renderer* renderer, size_t count);
bool append_str(SDL_Renderer* renderer, const std::string& text);
const SDL_Rect& input_rect() const;
std::string value() const;
bool readonly() const;
protected:
bool update_input(SDL_Renderer* renderer, SDL_Color fgclor);
private:
SdlInputWidget(const SdlInputWidget& other) = delete;
private:
Uint32 _flags;
std::string _text;
std::string _text_label;
SdlWidget _label;
SdlWidget _input;
bool _highlight;
bool _mouseover;
};

View File

@ -0,0 +1,276 @@
#include <assert.h>
#include "sdl_input_widgets.hpp"
static const Uint32 vpadding = 5;
static const Uint32 hpadding = 10;
SdlInputWidgetList::SdlInputWidgetList(const std::string& title,
const std::vector<std::string>& labels,
const std::vector<std::string>& initial,
const std::vector<Uint32>& flags)
{
assert(labels.size() == initial.size());
assert(labels.size() == flags.size());
const std::vector<int> buttonids = { INPUT_BUTTON_ACCEPT, INPUT_BUTTON_CANCEL };
const std::vector<std::string> buttonlabels = { "accept", "cancel" };
TTF_Init();
const size_t widget_width = 300;
const size_t widget_heigth = 50;
const size_t total_width = widget_width + widget_width;
const size_t input_height = labels.size() * (widget_heigth + vpadding) + vpadding;
const size_t total_height = input_height + widget_heigth;
_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
total_width, total_height, 0);
if (_window == nullptr)
{
widget_log_error(-1, "SDL_CreateWindow");
throw;
}
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
if (_renderer == nullptr)
{
widget_log_error(-1, "SDL_CreateRenderer");
throw;
}
for (size_t x = 0; x < labels.size(); x++)
_list.push_back(
{ _renderer, labels[x], initial[x], flags[x], x, widget_width, widget_heigth });
_buttons.populate(_renderer, buttonlabels, buttonids, static_cast<Sint32>(input_height),
static_cast<Sint32>(widget_width), static_cast<Sint32>(widget_heigth));
}
ssize_t SdlInputWidgetList::next(ssize_t current)
{
size_t iteration = 0;
size_t val = static_cast<size_t>(current);
do
{
if (iteration >= _list.size())
return -1;
if (iteration == 0)
{
if (current < 0)
val = 0;
else
val++;
}
else
val++;
iteration++;
val %= _list.size();
} while (!valid(static_cast<ssize_t>(val)));
return static_cast<ssize_t>(val);
}
bool SdlInputWidgetList::valid(ssize_t current) const
{
if (current < 0)
return false;
auto s = static_cast<size_t>(current);
if (s >= _list.size())
return false;
return !_list[s].readonly();
}
SdlInputWidget* SdlInputWidgetList::get(ssize_t index)
{
if (index < 0)
return nullptr;
auto s = static_cast<size_t>(index);
if (s >= _list.size())
return nullptr;
return &_list[s];
}
SdlInputWidgetList::~SdlInputWidgetList()
{
_list.clear();
_buttons.clear();
SDL_DestroyWindow(_window);
SDL_DestroyRenderer(_renderer);
TTF_Quit();
}
bool SdlInputWidgetList::update(SDL_Renderer* renderer)
{
for (auto& btn : _list)
{
if (!btn.update_label(renderer))
return false;
if (!btn.update_input(renderer))
return false;
}
return _buttons.update(renderer);
}
ssize_t SdlInputWidgetList::get_index(const SDL_MouseButtonEvent& button)
{
const Sint32 x = button.x;
const Sint32 y = button.y;
for (size_t i = 0; i < _list.size(); i++)
{
auto& cur = _list[i];
auto r = cur.input_rect();
if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
return i;
}
return -1;
}
int SdlInputWidgetList::run(std::vector<std::string>& result)
{
int res = -1;
ssize_t LastActiveTextInput = -1;
ssize_t CurrentActiveTextInput = next(-1);
try
{
bool running = true;
while (running)
{
if (!clear_window(_renderer))
throw;
if (!update(_renderer))
throw;
if (!_buttons.update(_renderer))
throw;
SDL_Event event = {};
SDL_WaitEvent(&event);
switch (event.type)
{
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_BACKSPACE:
{
auto cur = get(CurrentActiveTextInput);
if (cur)
{
if (!cur->remove_str(_renderer, 1))
throw;
}
}
break;
case SDLK_TAB:
CurrentActiveTextInput = next(CurrentActiveTextInput);
break;
case SDLK_RETURN:
case SDLK_RETURN2:
case SDLK_KP_ENTER:
running = false;
res = INPUT_BUTTON_ACCEPT;
break;
case SDLK_ESCAPE:
running = false;
res = INPUT_BUTTON_CANCEL;
break;
default:
break;
}
break;
case SDL_TEXTINPUT:
{
auto cur = get(CurrentActiveTextInput);
if (cur)
{
if (!cur->append_str(_renderer, event.text.text))
throw;
}
}
break;
case SDL_MOUSEMOTION:
{
auto TextInputIndex = get_index(event.button);
for (auto& cur : _list)
{
if (!cur.set_mouseover(_renderer, false))
throw;
}
if (TextInputIndex >= 0)
{
auto& cur = _list[static_cast<size_t>(TextInputIndex)];
if (!cur.set_mouseover(_renderer, true))
throw;
}
auto button = _buttons.get_selected(event.button);
if (button)
{
if (!button->highlight(_renderer))
throw;
}
}
break;
case SDL_MOUSEBUTTONDOWN:
{
auto val = get_index(event.button);
if (valid(val))
CurrentActiveTextInput = val;
auto button = _buttons.get_selected(event.button);
if (button)
{
running = false;
if (button->id() == INPUT_BUTTON_CANCEL)
res = INPUT_BUTTON_CANCEL;
else
res = INPUT_BUTTON_ACCEPT;
}
}
break;
case SDL_QUIT:
res = INPUT_BUTTON_CANCEL;
running = false;
break;
default:
break;
}
if (LastActiveTextInput != CurrentActiveTextInput)
{
if (CurrentActiveTextInput < 0)
SDL_StopTextInput();
else
SDL_StartTextInput();
LastActiveTextInput = CurrentActiveTextInput;
}
for (auto& cur : _list)
{
if (!cur.set_highlight(_renderer, false))
throw;
}
auto cur = get(CurrentActiveTextInput);
if (cur)
{
if (!cur->set_highlight(_renderer, true))
throw;
}
SDL_RenderPresent(_renderer);
}
for (auto& cur : _list)
result.push_back(cur.value());
}
catch (...)
{
}
return res;
}

View File

@ -0,0 +1,44 @@
#pragma once
#include <string>
#include <vector>
#include <SDL.h>
#include "sdl_input.hpp"
#include "sdl_buttons.hpp"
class SdlInputWidgetList
{
public:
SdlInputWidgetList(const std::string& title, const std::vector<std::string>& labels,
const std::vector<std::string>& initial, const std::vector<Uint32>& flags);
virtual ~SdlInputWidgetList();
int run(std::vector<std::string>& result);
protected:
bool update(SDL_Renderer* renderer);
ssize_t get_index(const SDL_MouseButtonEvent& button);
private:
SdlInputWidgetList(const SdlInputWidgetList& other) = delete;
SdlInputWidgetList(SdlInputWidgetList&& other) = delete;
private:
enum
{
INPUT_BUTTON_ACCEPT = 1,
INPUT_BUTTON_CANCEL = -2
};
private:
ssize_t next(ssize_t current);
bool valid(ssize_t current) const;
SdlInputWidget* get(ssize_t index);
private:
SDL_Window* _window;
SDL_Renderer* _renderer;
std::vector<SdlInputWidget> _list;
SdlButtonList _buttons;
};

View File

@ -0,0 +1,79 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#include <assert.h>
#include <stdbool.h>
#include <string>
#include <SDL.h>
#include <SDL_ttf.h>
#include "sdl_select.hpp"
#include "sdl_widget.hpp"
#include "sdl_button.hpp"
#include "sdl_buttons.hpp"
#include "sdl_input_widgets.hpp"
static const SDL_Color labelmouseovercolor = { 0, 0x80, 0, 0x60 };
static const SDL_Color labelbackgroundcolor = { 0x69, 0x66, 0x63, 0xff };
static const SDL_Color labelhighlightcolor = { 0xcd, 0xca, 0x35, 0x60 };
static const SDL_Color labelfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
SdlSelectWidget::SdlSelectWidget(SDL_Renderer* renderer, const std::string& label,
const SDL_Rect& rect)
: SdlWidget(renderer, rect, true), _text(label), _mouseover(false), _highlight(false)
{
update_text(renderer);
}
SdlSelectWidget::SdlSelectWidget(SdlSelectWidget&& other) noexcept
: SdlWidget(std::move(other)), _text(std::move(other._text)), _mouseover(other._mouseover),
_highlight(other._highlight)
{
}
SdlSelectWidget::~SdlSelectWidget()
{
}
bool SdlSelectWidget::set_mouseover(SDL_Renderer* renderer, bool mouseOver)
{
_mouseover = mouseOver;
return update_text(renderer);
}
bool SdlSelectWidget::set_highlight(SDL_Renderer* renderer, bool highlight)
{
_highlight = highlight;
return update_text(renderer);
}
bool SdlSelectWidget::update_text(SDL_Renderer* renderer)
{
assert(renderer);
std::vector<SDL_Color> colors = { labelbackgroundcolor };
if (_highlight)
colors.push_back(labelhighlightcolor);
if (_mouseover)
colors.push_back(labelmouseovercolor);
if (!fill(renderer, colors))
return false;
return SdlWidget::update_text(renderer, _text, labelfontcolor);
}

View File

@ -0,0 +1,46 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#pragma once
#include <string>
#include <vector>
#include <SDL.h>
#include "sdl_widget.hpp"
class SdlSelectWidget : public SdlWidget
{
public:
SdlSelectWidget(SDL_Renderer* renderer, const std::string& label, const SDL_Rect& rect);
SdlSelectWidget(SdlSelectWidget&& other) noexcept;
virtual ~SdlSelectWidget();
bool set_mouseover(SDL_Renderer* renderer, bool mouseOver);
bool set_highlight(SDL_Renderer* renderer, bool highlight);
bool update_text(SDL_Renderer* renderer);
private:
SdlSelectWidget(const SdlSelectWidget& other) = delete;
private:
std::string _text;
bool _mouseover;
bool _highlight;
};

View File

@ -0,0 +1,208 @@
#include "sdl_selectlist.hpp"
static const Uint32 vpadding = 5;
static const Uint32 hpadding = 10;
SdlSelectList::SdlSelectList(const std::string& title, const std::vector<std::string>& labels)
{
TTF_Init();
const size_t widget_height = 50;
const size_t widget_width = 600;
const size_t total_height = labels.size() * (widget_height + vpadding) + vpadding;
_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
widget_width, total_height + widget_height, 0);
if (_window == nullptr)
{
widget_log_error(-1, "SDL_CreateWindow");
throw;
}
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
if (_renderer == nullptr)
{
widget_log_error(-1, "SDL_CreateRenderer");
throw;
}
SDL_Rect rect = { 0, 0, widget_width, widget_height };
for (auto& label : labels)
{
_list.push_back({ _renderer, label, rect });
rect.y += widget_height + vpadding;
}
const std::vector<int> buttonids = { INPUT_BUTTON_ACCEPT, INPUT_BUTTON_CANCEL };
const std::vector<std::string> buttonlabels = { "accept", "cancel" };
_buttons.populate(_renderer, buttonlabels, buttonids, static_cast<Sint32>(total_height),
static_cast<Sint32>(widget_width / 2), static_cast<Sint32>(widget_height));
}
SdlSelectList::~SdlSelectList()
{
_list.clear();
_buttons.clear();
SDL_DestroyWindow(_window);
SDL_DestroyRenderer(_renderer);
TTF_Quit();
}
int SdlSelectList::run()
{
int res = -2;
ssize_t CurrentActiveTextInput = 0;
bool running = true;
try
{
while (running)
{
if (!clear_window(_renderer))
throw;
if (!update_text())
throw;
if (!_buttons.update(_renderer))
throw;
SDL_Event event = { 0 };
SDL_WaitEvent(&event);
switch (event.type)
{
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_UP:
case SDLK_BACKSPACE:
if (CurrentActiveTextInput > 0)
CurrentActiveTextInput--;
else
CurrentActiveTextInput = _list.size() - 1;
break;
case SDLK_DOWN:
case SDLK_TAB:
if (CurrentActiveTextInput < 0)
CurrentActiveTextInput = 0;
else
CurrentActiveTextInput++;
CurrentActiveTextInput = CurrentActiveTextInput % _list.size();
break;
case SDLK_RETURN:
case SDLK_RETURN2:
case SDLK_KP_ENTER:
running = false;
res = CurrentActiveTextInput;
break;
case SDLK_ESCAPE:
running = false;
res = INPUT_BUTTON_CANCEL;
break;
default:
break;
}
break;
case SDL_MOUSEMOTION:
{
ssize_t TextInputIndex = get_index(event.button);
reset_mouseover();
if (TextInputIndex >= 0)
{
auto& cur = _list[TextInputIndex];
if (!cur.set_mouseover(_renderer, true))
throw;
}
auto button = _buttons.get_selected(event.button);
if (button)
{
if (!button->highlight(_renderer))
throw;
}
}
break;
case SDL_MOUSEBUTTONDOWN:
{
auto button = _buttons.get_selected(event.button);
if (button)
{
running = false;
if (button->id() == INPUT_BUTTON_CANCEL)
res = INPUT_BUTTON_CANCEL;
else
res = CurrentActiveTextInput;
}
else
{
CurrentActiveTextInput = get_index(event.button);
}
}
break;
case SDL_QUIT:
res = INPUT_BUTTON_CANCEL;
running = false;
break;
default:
break;
}
reset_highlight();
if (CurrentActiveTextInput >= 0)
{
auto& cur = _list[CurrentActiveTextInput];
if (!cur.set_highlight(_renderer, true))
throw;
}
SDL_RenderPresent(_renderer);
}
}
catch (...)
{
return -1;
}
return res;
}
ssize_t SdlSelectList::get_index(const SDL_MouseButtonEvent& button)
{
const Sint32 x = button.x;
const Sint32 y = button.y;
for (size_t i = 0; i < _list.size(); i++)
{
auto& cur = _list[i];
auto r = cur.rect();
if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
return i;
}
return -1;
}
bool SdlSelectList::update_text()
{
for (auto& cur : _list)
{
if (!cur.update_text(_renderer))
return false;
}
return true;
}
void SdlSelectList::reset_mouseover()
{
for (auto& cur : _list)
{
cur.set_mouseover(_renderer, false);
}
}
void SdlSelectList::reset_highlight()
{
for (auto& cur : _list)
{
cur.set_highlight(_renderer, false);
}
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <string>
#include <vector>
#include <SDL.h>
#include "sdl_select.hpp"
#include "sdl_button.hpp"
#include "sdl_buttons.hpp"
class SdlSelectList
{
public:
SdlSelectList(const std::string& title, const std::vector<std::string>& labels);
virtual ~SdlSelectList();
int run();
private:
SdlSelectList(const SdlSelectList& other) = delete;
SdlSelectList(SdlSelectList&& other) = delete;
private:
enum
{
INPUT_BUTTON_ACCEPT = 0,
INPUT_BUTTON_CANCEL = -2
};
private:
ssize_t get_index(const SDL_MouseButtonEvent& button);
bool update_text();
void reset_mouseover();
void reset_highlight();
private:
SDL_Window* _window;
SDL_Renderer* _renderer;
std::vector<SdlSelectWidget> _list;
SdlButtonList _buttons;
};

View File

@ -0,0 +1,170 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <SDL.h>
#include <SDL_ttf.h>
#include "sdl_widget.hpp"
#include "../sdl_utils.hpp"
#include "font/font_writer.hpp"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("SDL.widget")
static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
static const Uint32 vpadding = 5;
static const Uint32 hpadding = 10;
SdlWidget::SdlWidget(SDL_Renderer* renderer, const SDL_Rect& rect, bool input)
: _font(nullptr), _rect(rect), _input(input)
{
assert(renderer);
auto font = create_and_return_temorary_font();
_font = TTF_OpenFont(font.c_str(), 64);
}
SdlWidget::SdlWidget(SdlWidget&& other) noexcept
: _font(std::move(other._font)), _rect(std::move(other._rect))
{
other._font = nullptr;
}
SdlWidget::~SdlWidget()
{
TTF_CloseFont(_font);
}
bool SdlWidget::error_ex(Uint32 res, const char* what, const char* file, size_t line,
const char* fkt)
{
static wLog* log = nullptr;
if (!log)
log = WLog_Get(TAG);
return sdl_log_error_ex(res, log, what, file, line, fkt);
}
static bool draw_rect(SDL_Renderer* renderer, const SDL_Rect* rect, SDL_Color color)
{
const int drc = SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
return false;
const int rc = SDL_RenderFillRect(renderer, rect);
return !widget_log_error(rc, "SDL_RenderFillRect");
}
bool SdlWidget::fill(SDL_Renderer* renderer, SDL_Color color)
{
std::vector<SDL_Color> colors = { color };
return fill(renderer, colors);
}
bool SdlWidget::fill(SDL_Renderer* renderer, const std::vector<SDL_Color>& colors)
{
assert(renderer);
SDL_BlendMode mode;
SDL_GetRenderDrawBlendMode(renderer, &mode);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
for (auto color : colors)
{
draw_rect(renderer, &_rect, color);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD);
}
SDL_SetRenderDrawBlendMode(renderer, mode);
return true;
}
bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor,
SDL_Color bgcolor)
{
assert(renderer);
if (!fill(renderer, bgcolor))
return false;
return update_text(renderer, text, fgcolor);
}
const SDL_Rect& SdlWidget::rect() const
{
return _rect;
}
bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor)
{
if (text.empty())
return true;
auto surface = TTF_RenderUTF8_Blended(_font, text.c_str(), fgcolor);
if (!surface)
return !widget_log_error(-1, "TTF_RenderText_Blended");
auto texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
if (!texture)
return !widget_log_error(-1, "SDL_CreateTextureFromSurface");
SDL_Rect src{};
TTF_SizeUTF8(_font, text.c_str(), &src.w, &src.h);
/* Do some magic:
* - Add padding before and after text
* - if text is too long only show the last elements
* - if text is too short only update used space
*/
auto dst = _rect;
dst.x += hpadding;
dst.w -= 2 * hpadding;
const float scale = static_cast<float>(dst.h) / static_cast<float>(src.h);
const float sws = static_cast<float>(src.w) * scale;
const float dws = static_cast<float>(dst.w) / scale;
if (static_cast<float>(dst.w) > sws)
dst.w = static_cast<int>(sws);
if (static_cast<float>(src.w) > dws)
{
src.x = src.w - static_cast<int>(dws);
src.w = static_cast<int>(dws);
}
const int rc = SDL_RenderCopy(renderer, texture, &src, &dst);
SDL_DestroyTexture(texture);
if (rc < 0)
return !widget_log_error(rc, "SDL_RenderCopy");
return true;
}
bool clear_window(SDL_Renderer* renderer)
{
assert(renderer);
const int drc = SDL_SetRenderDrawColor(renderer, backgroundcolor.r, backgroundcolor.g,
backgroundcolor.b, backgroundcolor.a);
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
return false;
const int rcls = SDL_RenderClear(renderer);
return !widget_log_error(rcls, "SDL_RenderClear");
}

View File

@ -0,0 +1,64 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Client helper dialogs
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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.
*/
#pragma once
#include <string>
#include <stdbool.h>
#include <vector>
#include <SDL.h>
#include <SDL_ttf.h>
#if defined(_MSC_VER)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
class SdlWidget
{
public:
SdlWidget(SDL_Renderer* renderer, const SDL_Rect& rect, bool input);
SdlWidget(SdlWidget&& other) noexcept;
virtual ~SdlWidget();
bool fill(SDL_Renderer* renderer, SDL_Color color);
bool fill(SDL_Renderer* renderer, const std::vector<SDL_Color>& colors);
bool update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor);
bool update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor,
SDL_Color bgcolor);
const SDL_Rect& rect() const;
public:
#define widget_log_error(res, what) SdlWidget::error_ex(res, what, __FILE__, __LINE__, __FUNCTION__)
static bool error_ex(Uint32 res, const char* what, const char* file, size_t line,
const char* fkt);
private:
SdlWidget(const SdlWidget& other) = delete;
private:
TTF_Font* _font;
SDL_Rect _rect;
bool _input;
};
bool clear_window(SDL_Renderer* renderer);

View File

@ -54,6 +54,7 @@
#include "sdl_kbd.hpp"
#include "sdl_touch.hpp"
#include "sdl_pointer.hpp"
#include "dialogs/sdl_dialogs.hpp"
#ifdef WITH_WEBVIEW
#include "sdl_webview.hpp"
@ -686,8 +687,19 @@ static int sdl_run(SdlContext* sdl)
while (!freerdp_shall_disconnect_context(sdl->context()))
{
SDL_Event windowEvent = { 0 };
while (!freerdp_shall_disconnect_context(sdl->context()) && SDL_WaitEvent(&windowEvent))
while (!freerdp_shall_disconnect_context(sdl->context()) && SDL_PollEvent(nullptr))
{
/* Only poll standard SDL events and SDL_USEREVENTS meant to create dialogs.
* do not process the dialog return value events here.
*/
const int prc = SDL_PeepEvents(&windowEvent, 1, SDL_GETEVENT, SDL_FIRSTEVENT,
SDL_USEREVENT_SCARD_DIALOG);
if (prc < 0)
{
if (sdl_log_error(prc, sdl->log, "SDL_PeepEvents"))
continue;
}
#if defined(WITH_DEBUG_SDL_EVENTS)
SDL_Log("got event %s [0x%08" PRIx32 "]", sdl_event_type_str(windowEvent.type),
windowEvent.type);
@ -770,6 +782,31 @@ static int sdl_run(SdlContext* sdl)
case SDL_APP_WILLENTERFOREGROUND:
sdl_redraw(sdl);
break;
case SDL_USEREVENT_CERT_DIALOG:
{
auto title = static_cast<const char*>(windowEvent.user.data1);
auto msg = static_cast<const char*>(windowEvent.user.data2);
sdl_cert_dialog_show(title, msg);
}
break;
case SDL_USEREVENT_SHOW_DIALOG:
{
auto title = static_cast<const char*>(windowEvent.user.data1);
auto msg = static_cast<const char*>(windowEvent.user.data2);
sdl_message_dialog_show(title, msg, windowEvent.user.code);
}
break;
case SDL_USEREVENT_SCARD_DIALOG:
{
auto title = static_cast<const char*>(windowEvent.user.data1);
auto msg = static_cast<const char**>(windowEvent.user.data2);
sdl_scard_dialog_show(title, windowEvent.user.code, msg);
}
break;
case SDL_USEREVENT_AUTH_DIALOG:
sdl_auth_dialog_show(
reinterpret_cast<const SDL_UserAuthArg*>(windowEvent.padding));
break;
case SDL_USEREVENT_UPDATE:
{
auto context = static_cast<rdpContext*>(windowEvent.user.data1);
@ -1105,20 +1142,6 @@ static void sdl_client_global_uninit(void)
#endif
}
static int sdl_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;
auto sdl = get_context(instance->context);
WLog_Print(sdl->log, WLOG_INFO, "Logon Error Info %s [%s]", str_data, str_type);
return 1;
}
static BOOL sdl_client_new(freerdp* instance, rdpContext* context)
{
auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
@ -1134,9 +1157,9 @@ static BOOL sdl_client_new(freerdp* instance, rdpContext* context)
instance->PostConnect = sdl_post_connect;
instance->PostDisconnect = sdl_post_disconnect;
instance->PostFinalDisconnect = sdl_post_final_disconnect;
instance->AuthenticateEx = client_cli_authenticate_ex;
instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
instance->AuthenticateEx = sdl_authenticate_ex;
instance->VerifyCertificateEx = sdl_verify_certificate_ex;
instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
instance->LogonErrorInfo = sdl_logon_error_info;
#ifdef WITH_WEBVIEW
instance->GetAccessToken = sdl_webview_get_access_token;

View File

@ -95,6 +95,14 @@ const char* sdl_event_type_str(Uint32 type)
EV_CASE_STR(SDL_RENDER_DEVICE_RESET);
EV_CASE_STR(SDL_USEREVENT);
EV_CASE_STR(SDL_USEREVENT_CERT_DIALOG);
EV_CASE_STR(SDL_USEREVENT_CERT_RESULT);
EV_CASE_STR(SDL_USEREVENT_SHOW_DIALOG);
EV_CASE_STR(SDL_USEREVENT_SHOW_RESULT);
EV_CASE_STR(SDL_USEREVENT_AUTH_DIALOG);
EV_CASE_STR(SDL_USEREVENT_AUTH_RESULT);
EV_CASE_STR(SDL_USEREVENT_SCARD_DIALOG);
EV_CASE_STR(SDL_USEREVENT_SCARD_RESULT);
EV_CASE_STR(SDL_USEREVENT_UPDATE);
EV_CASE_STR(SDL_USEREVENT_CREATE_WINDOWS);
EV_CASE_STR(SDL_USEREVENT_WINDOW_RESIZEABLE);
@ -104,6 +112,7 @@ const char* sdl_event_type_str(Uint32 type)
EV_CASE_STR(SDL_USEREVENT_POINTER_POSITION);
EV_CASE_STR(SDL_USEREVENT_POINTER_SET);
EV_CASE_STR(SDL_USEREVENT_QUIT);
EV_CASE_STR(SDL_LASTEVENT);
default:
return "SDL_UNKNOWNEVENT";
@ -144,6 +153,48 @@ BOOL sdl_push_user_event(Uint32 type, ...)
event->type = type;
switch (type)
{
case SDL_USEREVENT_AUTH_RESULT:
{
SDL_UserAuthArg* arg = (SDL_UserAuthArg*)ev.padding;
arg->user = va_arg(ap, char*);
arg->domain = va_arg(ap, char*);
arg->password = va_arg(ap, char*);
arg->result = va_arg(ap, Sint32);
}
break;
case SDL_USEREVENT_AUTH_DIALOG:
{
SDL_UserAuthArg* arg = (SDL_UserAuthArg*)ev.padding;
arg->title = va_arg(ap, char*);
arg->user = va_arg(ap, char*);
arg->domain = va_arg(ap, char*);
arg->password = va_arg(ap, char*);
arg->result = va_arg(ap, Sint32);
}
break;
case SDL_USEREVENT_SCARD_DIALOG:
{
event->data1 = va_arg(ap, char*);
event->data2 = va_arg(ap, char**);
event->code = va_arg(ap, Sint32);
}
break;
case SDL_USEREVENT_SCARD_RESULT:
case SDL_USEREVENT_SHOW_RESULT:
case SDL_USEREVENT_CERT_RESULT:
event->code = va_arg(ap, Sint32);
break;
case SDL_USEREVENT_SHOW_DIALOG:
event->data1 = va_arg(ap, char*);
event->data2 = va_arg(ap, char*);
event->code = va_arg(ap, Sint32);
break;
case SDL_USEREVENT_CERT_DIALOG:
event->data1 = va_arg(ap, char*);
event->data2 = va_arg(ap, char*);
break;
case SDL_USEREVENT_UPDATE:
event->data1 = va_arg(ap, void*);
break;

View File

@ -64,9 +64,29 @@ enum
SDL_USEREVENT_POINTER_DEFAULT,
SDL_USEREVENT_POINTER_POSITION,
SDL_USEREVENT_POINTER_SET,
SDL_USEREVENT_QUIT
SDL_USEREVENT_QUIT,
SDL_USEREVENT_CERT_DIALOG,
SDL_USEREVENT_SHOW_DIALOG,
SDL_USEREVENT_AUTH_DIALOG,
SDL_USEREVENT_SCARD_DIALOG,
SDL_USEREVENT_CERT_RESULT,
SDL_USEREVENT_SHOW_RESULT,
SDL_USEREVENT_AUTH_RESULT,
SDL_USEREVENT_SCARD_RESULT
};
typedef struct
{
Uint32 type;
Uint32 timestamp;
char* title;
char* user;
char* domain;
char* password;
Sint32 result;
} SDL_UserAuthArg;
BOOL sdl_push_user_event(Uint32 type, ...);
const char* sdl_event_type_str(Uint32 type);

View File

@ -0,0 +1,28 @@
set(MODULE_NAME "TestSDL")
set(MODULE_PREFIX "TEST_SDL")
set(DRIVER ${MODULE_NAME}.c)
set(TEST_SRCS
TestSDLDialogs.c
)
create_test_sourcelist(SRCS
${DRIVER}
${TEST_SRCS})
add_executable(${MODULE_NAME} ${SRCS})
set(LIBS ${LIBS} freerdp-client freerdp SDL_client)
target_link_libraries(${MODULE_NAME} ${LIBS})
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Client/Test")

View File

@ -0,0 +1,110 @@
#include <freerdp/config.h>
#include "../sdl_dialogs.h"
#include "../sdl_select.h"
#include "../sdl_input.h"
#include "../sdl_utils.h"
static void array_free(char** array, size_t count)
{
if (!array)
return;
for (size_t x = 0; x < count; x++)
free(array[x]);
free(array);
}
static char* allocate_random_str(void)
{
size_t len = (rand() % 32) + 3;
char* str = calloc(len + 1, sizeof(char));
if (!str)
return NULL;
for (size_t x = 0; x < len; x++)
{
char cur = 'a' + rand() % 26;
str[x] = cur;
}
return str;
}
static char** allocate_random(size_t count)
{
char** array = calloc(count, sizeof(char*));
if (!array)
return NULL;
for (size_t x = 0; x < count; x++)
array[x] = allocate_random_str();
return array;
}
static BOOL test_select_dialog(wLog* log)
{
BOOL res = FALSE;
const size_t count = 7;
char** labels = allocate_random(count);
if (!labels)
goto fail;
const int irc = SDL_Init(SDL_INIT_VIDEO);
if (sdl_log_error(irc, log, "SDL_Init"))
goto fail;
const int rc = sdl_select_get("sometitle", count, (const char**)labels);
if (rc < 0)
goto fail;
res = TRUE;
fail:
array_free(labels, count);
SDL_Quit();
return res;
}
static BOOL test_input_dialog(wLog* log)
{
BOOL res = FALSE;
const size_t count = 7;
char** labels = allocate_random(count);
char** initial = allocate_random(count);
Uint32* flags = calloc(count, sizeof(Uint32));
char** result = calloc(count, sizeof(char*));
if (!labels || !initial || !flags || !result)
goto fail;
flags[0] = SDL_INPUT_MASK;
const int irc = SDL_Init(SDL_INIT_VIDEO);
if (sdl_log_error(irc, log, "SDL_Init"))
goto fail;
const int rc = sdl_input_get("sometitle", count, (const char**)labels, (const char**)initial,
flags, result);
if (rc < 0)
goto fail;
res = TRUE;
fail:
array_free(labels, count);
array_free(initial, count);
array_free(result, count);
free(flags);
SDL_Quit();
return res;
}
int TestSDLDialogs(int argc, char* argv[])
{
#if 0
wLog* log = WLog_Get("TestSDLDialogs");
if (!test_input_dialog(log))
return -1;
if (!test_select_dialog(log))
return -1;
#endif
return 0;
}

163
cmake/FindSDL2TTF.cmake Normal file
View File

@ -0,0 +1,163 @@
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2DIR is an environment variable that would
# correspond to the ./configure --prefix=$SDL2DIR
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
SET(SDL2TTF_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_PATH(SDL2TTF_INCLUDE_DIR SDL_ttf.h
HINTS
$ENV{SDL2TTFDIR}
PATH_SUFFIXES include/SDL2 include
PATHS ${SDL2TTF_SEARCH_PATHS}
)
FIND_LIBRARY(SDL2TTF_LIBRARY_TEMP
NAMES SDL2_ttf
HINTS
$ENV{SDL2TTFDIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2TTF_SEARCH_PATHS}
)
IF(NOT SDL2TTF_BUILDING_LIBRARY)
IF(NOT ${SDL2TTF_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2TTFmain. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2TTFmain for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2TTFMAIN_LIBRARY
NAMES SDL2_ttf
HINTS
$ENV{SDL2TTFDIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2TTF_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2TTF_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2TTF_BUILDING_LIBRARY)
# SDL2TTF may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2TTFmain -lSDL2TTF -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
IF(SDL2TTF_LIBRARY_TEMP)
# For SDL2TTFmain
IF(NOT SDL2TTF_BUILDING_LIBRARY)
IF(SDL2TTFMAIN_LIBRARY)
SET(SDL2TTF_LIBRARY_TEMP ${SDL2TTFMAIN_LIBRARY} ${SDL2TTF_LIBRARY_TEMP})
ENDIF(SDL2TTFMAIN_LIBRARY)
ENDIF(NOT SDL2TTF_BUILDING_LIBRARY)
# For OS X, SDL2TTF uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2TTF_LIBRARY_TEMP ${SDL2TTF_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2TTF_LIBRARY_TEMP ${SDL2TTF_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2TTF_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2TTF_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2TTF_LIBRARY ${SDL2TTF_LIBRARY_TEMP} CACHE STRING "Where the SDL2TTF Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2TTF_LIBRARY_TEMP "${SDL2TTF_LIBRARY_TEMP}" CACHE INTERNAL "")
ENDIF(SDL2TTF_LIBRARY_TEMP)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2TTF REQUIRED_VARS SDL2TTF_LIBRARY SDL2TTF_INCLUDE_DIR)