mirror of https://github.com/FreeRDP/FreeRDP
[client,sdl] add user dialogs
This commit is contained in:
parent
a0d38914d6
commit
97415f0d52
|
@ -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)
|
||||
|
|
|
@ -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})
|
|
@ -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)
|
|
@ -0,0 +1,2 @@
|
|||
all:
|
||||
cc -o convert_font_to_c convert_font_to_c.c
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
extern const std::vector<unsigned char> font_buffer;
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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");
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
Loading…
Reference in New Issue