Compare commits

...

74 commits
1.63 ... master

Author SHA1 Message Date
bearoso
0525ea043e
Merge pull request #969 from yeager/master
Adding Swedish translation
2025-02-24 18:12:29 -06:00
Daniel Nylander
43d01935a9
Update sv.po 2025-02-09 08:41:54 +01:00
Daniel Nylander
db1b823558
Add files via upload 2025-02-09 08:39:58 +01:00
Daniel Nylander
0a567bd4fc
Add files via upload 2025-02-08 19:43:42 +01:00
Daniel Nylander
e3af507c5b
Add files via upload 2025-02-08 19:08:32 +01:00
Daniel Nylander
72922ce708
Adding Swedish translation 2025-02-08 18:09:59 +01:00
BearOso
663738341a qt,gtk/vulkan: Add VK_EXT_present_wait support again. 2025-01-24 15:55:41 -06:00
BearOso
191cdf462a vulkan/wayland: Use mailbox present mode.
FIFO can stall when other windows obscure the main window.
2025-01-24 13:33:59 -06:00
BearOso
5df649d49a Qt: Use SDL 3.2.0 tag. 2025-01-21 16:38:16 -06:00
bearoso
48fe934463
Merge pull request #967 from gilchavezm/patch-3
Qt: Update EmuApplication.cpp: handle Quit binding
2024-12-17 20:25:35 -06:00
Gilberto Chavez-Martinez
6794f51461
Update EmuApplication.cpp: handle Quit binding
Added handling of binding for Quit.
2024-12-17 19:24:07 -05:00
BearOso
b83f789343 sdl3/audio: Don't double the buffer size requested. 2024-12-13 20:06:07 -06:00
BearOso
1dbfd2e141 Qt: Use FetchContent for SDL3 on Windows or if not found. 2024-12-13 19:47:15 -06:00
BearOso
73b71c865b Qt: Allow building on unix without SDL video support. 2024-12-13 19:12:54 -06:00
BearOso
959da2aa04 Qt: Free bindings. 2024-12-13 19:08:09 -06:00
BearOso
03ec60cfda Qt: Fix up SDL3 button binding. 2024-12-13 19:04:56 -06:00
BearOso
fa94c74b84 common/audio/sdl3: Use a mutex for buffer accesses. 2024-12-13 17:23:20 -06:00
BearOso
56e58cdf99 Qt: Switch to SDL3 for audio and input.
SDL3 is not a submodule in the git tree yet, so as to not cause
an extra checkout for the other ports. It will eventually go in
external/SDL.
2024-12-13 17:14:34 -06:00
BearOso
2ffc66c33c Gtk/Wayland: Only allow one pause state change per focus change.
Check whether we've already paused for it.

Gtk/Wayland sends multiple focus_notify events with the same
state, even when it doesn't change. This occurs when popup
menus are opened and closed.
2024-12-09 15:32:34 -06:00
BearOso
9be3ed49a8 Qt/Vulkan: Don't show canvas before embedding.
It would briefly pop up as a new window.
2024-12-08 15:56:09 -06:00
BearOso
0e309e5c7c Gtk/Qt/Vulkan: Don't set vsync before swapping.
This destroys the swapchain.
2024-12-06 15:10:54 -06:00
BearOso
a7d59843da Qt: Adapt to 6.8 changes for Wayland.
Remove parent stackwidget. Don't set native properties for the render,
widget because they apply to the main window instead. Subsurfaces
don't need that anyway.
2024-12-06 14:58:22 -06:00
BearOso
fd05ca7df5 Qt: Show paths in native format on Win32. 2024-10-28 17:32:29 -05:00
BearOso
81f189cf57 Qt: Fix build on Windows. 2024-10-28 17:07:10 -05:00
BearOso
b5d9217881 Win32: Don't build fmtlib.
Windows port doesn't use it.
2024-10-26 20:29:21 -05:00
BearOso
e6c4f617cc Vulkan: Move descriptor pool out of context. 2024-10-26 20:26:47 -05:00
BearOso
20768ed8b2 Remove accidental commit of xbrz. 2024-10-25 18:58:49 -05:00
BearOso
8a33829bce external: Update fmtlib. 2024-10-25 18:54:14 -05:00
BearOso
f6a46f5831 Clean up unused headers. 2024-10-22 14:58:50 -05:00
BearOso
170a6aa1a4 Vulkan: Favor default initializers. 2024-10-22 14:27:23 -05:00
BearOso
932b093d98 Gtk/OpenGL: Fix context resize broken in is_x11/is_wayland change. 2024-10-20 19:30:15 -05:00
BearOso
c6e0fbf866 Vulkan: Remove unused line. 2024-10-17 15:21:31 -05:00
BearOso
14910960eb Vulkan: Add common function files. 2024-10-17 15:16:43 -05:00
BearOso
79f6e911f3 Vulkan: Use helper for image transitions. 2024-10-17 15:15:21 -05:00
BearOso
39c0f8418b Vulkan: Use recommended access flags for image layout transitions. 2024-10-17 13:48:46 -05:00
BearOso
71f2ba2d7e Gtk: Confused is_wayland/is_x11. 2024-10-16 13:18:19 -05:00
BearOso
5b1f20ec52 Gtk: Use is_x11 function. 2024-10-14 20:45:44 -05:00
BearOso
1fc9f26522 Gtk: Fix refresh rate detection break. 2024-10-14 10:16:05 -05:00
BearOso
0c228f0e0c Gtk: Use a Wayland check function. 2024-10-12 17:06:07 -05:00
BearOso
b19e31c83f Gtk: Get Superscope working. 2024-10-12 17:05:26 -05:00
BearOso
8028d3b6ca S-DSP: Initialize a couple registers to fix Magical Drop. 2024-10-11 13:53:24 -05:00
BearOso
13824a6ef4 Vulkan: Allocate swapchain on context create. 2024-10-07 14:43:52 -05:00
BearOso
befb0ba768 Qt: Use C++20. 2024-10-07 14:24:26 -05:00
BearOso
14c434d40d Vulkan: Use references in Swapchain. 2024-10-05 17:46:01 -05:00
BearOso
08403d47d1 Vulkan/win32: Fix build errors. 2024-10-03 11:27:07 -05:00
BearOso
febcf27482 Vulkan: Separate context, surface, and swapchain. 2024-10-03 11:19:42 -05:00
OV2
72e4946410 win32: use selected bank for slot save/load, display both slot and bank
in info string (#953)
2024-09-27 21:10:25 +02:00
BearOso
0727b4a474 Gtk: Clean up Snes9xWindow a bit. 2024-09-15 14:35:23 -05:00
bearoso
e06f1887d4
Merge pull request #950 from Thorarin/load-preview-fix
Fix load with preview file names and time stamps on extra banks
2024-09-15 10:18:26 -05:00
BearOso
9ed6f9d86b Screenshot: Don't use colon in filename. Use localtime
instead of localtime_r/s. Enable screenshots on Qt build.
2024-09-15 10:14:46 -05:00
Marcel Veldhuizen
d5ba1c6017 Fix load with preview file names and time stamps on extra banks 2024-09-14 23:32:58 +02:00
BearOso
0c547f3486 Gtk/Wayland: Different workaround for Gtk damage bug.
Instead of completely shutting down the display driver, shrink the
subsurface when removing fullscreen so that when the parent window
sends events when it receives damage or is resized.
2024-09-12 14:21:03 -05:00
BearOso
ea243051ed Save screenshot files with date for easier sorting. 2024-09-11 19:22:45 -05:00
BearOso
ebd9df46ec Gtk: Avoid saving the size while fullscreening occurs. 2024-09-11 14:45:07 -05:00
BearOso
5449e2d3c5 vulkan: Fix compile errors reported in Issue #946. 2024-09-06 13:35:46 -05:00
BearOso
645a4712e7 vulkan: Remove waiting extensions.
I guess these aren't widely supported.
2024-09-05 18:09:11 -05:00
BearOso
15ae9de25b Qt/Vulkan: Don't try to draw black any more. 2024-09-05 16:10:06 -05:00
BearOso
fb89cbf4c4 vulkan: Tidy a bit. 2024-09-05 16:07:37 -05:00
BearOso
c8895c8cdb vulkan: Add support for VK_KHR_present_wait
May have a tighter CPU-GPU sync for lower latency.
2024-09-02 13:17:08 -05:00
BearOso
8559143576 Fix compile on Windows. 2024-08-10 18:03:55 -05:00
BearOso
1773782575 Move vulkan to common/video. 2024-08-10 17:41:47 -05:00
BearOso
9f7173f819 Move shaders directory into common/video/opengl. 2024-08-10 17:08:57 -05:00
BearOso
5c7847acbb Sort common OpenGL and Wayland files into folders. 2024-08-10 17:00:40 -05:00
BearOso
8b1d67397e Qt: Experiment with alternate widget while a game isn't running. 2024-08-02 18:10:03 -05:00
BearOso
e92b93ca9c libretro: Clear SRAM after loading ROM. 2024-07-29 15:57:03 -05:00
BearOso
5d9f5b061b Qt: Output S9xMessage to console. 2024-07-29 15:57:03 -05:00
BearOso
cc49a06c77 external: Update stb_image.h to reduce warnings 2024-07-29 15:57:03 -05:00
bearoso
4a20cfc024
Merge pull request #940 from lgv5/master
Gtk: Fix build on 32-bits systems.
2024-07-28 17:34:35 -05:00
Lucas Gabriel Vuotto
bff02194a7 Gtk: Fix build on 32-bits systems. 2024-07-28 20:52:20 +00:00
BearOso
8a9b8cfcfd SA1: Change mapping type for banks 40->4f on SA1. 2024-07-26 15:12:58 -05:00
BearOso
881eeaed9a Update some version strings. 2024-07-14 14:31:51 -05:00
Michael Buckley
7c9c220931 macOS: Fix keyboard settings warning 2024-07-14 12:27:08 -07:00
Michael Buckley
43b6efb12b MacOS: Fix building in Xcode 15. 2024-07-14 11:38:55 -07:00
OV2
18096d9f68 imgui: add movie frame count and watches display (#167) 2024-07-14 02:20:47 +02:00
132 changed files with 12878 additions and 9153 deletions

View file

@ -1245,6 +1245,8 @@ void SPC_DSP::load( uint8_t const regs [register_count] )
{
memcpy( m.external_regs, regs, sizeof m.regs );
memset( m.regs, 0, sizeof m.regs);
m.regs[66] = 0x01;
m.regs[82] = 0x01;
m.regs[r_flg] = 0xE0;
memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );

View file

@ -0,0 +1,120 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "s9x_sound_driver_sdl3.hpp"
#include "SDL3/SDL_audio.h"
#include <cstdio>
#include <vector>
bool S9xSDL3SoundDriver::write_samples(int16_t *data, int samples)
{
bool retval = true;
mutex.lock();
auto empty = buffer.space_empty();
if (samples > empty)
{
retval = false;
buffer.dump(buffer.buffer_size / 2 - empty);
}
buffer.push(data, samples);
mutex.unlock();
return retval;
}
void S9xSDL3SoundDriver::mix(int req, int total)
{
if (tmp.size() < req / 2)
tmp.resize(req / 2);
mutex.lock();
if (buffer.avail() >= req / 2)
{
buffer.read((int16_t *)(tmp.data()), req / 2);
mutex.unlock();
SDL_PutAudioStreamData(stream, tmp.data(), req);
}
else
{
auto avail = buffer.avail();
buffer.read((int16_t *)(tmp.data()), avail);
buffer.add_silence(buffer.buffer_size / 2);
mutex.unlock();
SDL_PutAudioStreamData(stream, tmp.data(), avail * 2);
}
}
S9xSDL3SoundDriver::S9xSDL3SoundDriver()
{
stream = nullptr;
}
S9xSDL3SoundDriver::~S9xSDL3SoundDriver()
{
deinit();
}
void S9xSDL3SoundDriver::init()
{
SDL_InitSubSystem(SDL_INIT_AUDIO);
stop();
}
void S9xSDL3SoundDriver::deinit()
{
stop();
SDL_DestroyAudioStream(stream);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
void S9xSDL3SoundDriver::start()
{
SDL_ResumeAudioStreamDevice(stream);
}
void S9xSDL3SoundDriver::stop()
{
SDL_PauseAudioStreamDevice(stream);
}
bool S9xSDL3SoundDriver::open_device(int playback_rate, int buffer_size)
{
audiospec = {};
audiospec.freq = playback_rate;
audiospec.channels = 2;
audiospec.format = SDL_AUDIO_S16;
printf("SDL sound driver initializing...\n");
stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
&audiospec,
[](void *userdata, SDL_AudioStream *stream, int addl, int total) {
((S9xSDL3SoundDriver *)userdata)->mix(addl, total);
}, this);
printf("OK\n");
if (buffer_size < 32)
buffer_size = 32;
buffer.resize(buffer_size * 2 * audiospec.freq / 1000);
return true;
}
int S9xSDL3SoundDriver::space_free()
{
auto space_empty = buffer.space_empty();
return space_empty;
}
std::pair<int, int> S9xSDL3SoundDriver::buffer_level()
{
std::pair<int, int> level = { buffer.space_empty(), buffer.buffer_size };
return level;
}

View file

@ -0,0 +1,46 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __S9X_SOUND_DRIVER_SDL3_HPP
#define __S9X_SOUND_DRIVER_SDL3_HPP
#include "SDL3/SDL.h"
// SDL.h may include altivec.h which redefines vector and bool
#undef vector
#undef bool
#include "s9x_sound_driver.hpp"
#include "../../apu/resampler.h"
#include <mutex>
#include <cstdint>
#include <vector>
class S9xSDL3SoundDriver : public S9xSoundDriver
{
public:
S9xSDL3SoundDriver();
~S9xSDL3SoundDriver();
void init() override;
void deinit() override;
bool open_device(int playback_rate, int buffer_size) override;
void start() override;
void stop() override;
bool write_samples(int16_t *data, int samples) override;
int space_free() override;
std::pair<int, int> buffer_level() override;
private:
void mix(int req, int total);
SDL_AudioStream *stream;
SDL_AudioSpec audiospec;
Resampler buffer;
std::mutex mutex;
std::vector<int16_t> tmp;
};
#endif /* __S9X_SOUND_DRIVER_SDL3_HPP */

View file

@ -11,7 +11,7 @@
#include <map>
#include "glsl.h"
#include "shader_helpers.h"
#include "../vulkan/slang_helpers.hpp"
#include "common/video/vulkan/slang_helpers.hpp"
#include "shader_platform.h"
#ifndef _MSC_VER
#include <unistd.h>

View file

@ -8,7 +8,7 @@
#define __GLSL_H
#include "snes9x.h"
#include "../vulkan/slang_preset_ini.hpp"
#include "common/video/vulkan/slang_preset_ini.hpp"
#include "shader_platform.h"
#include <deque>
#include <limits.h>

View file

@ -145,3 +145,13 @@ void WaylandEGLContext::swap_interval(int frames)
{
eglSwapInterval(egl_display, frames);
}
void WaylandEGLContext::shrink()
{
wayland_surface->shrink();
}
void WaylandEGLContext::regrow()
{
wayland_surface->regrow();
}

View file

@ -8,7 +8,7 @@
#define __WAYLAND_EGL_CONTEXT_H
#include "opengl_context.hpp"
#include "wayland_surface.hpp"
#include "common/video/wayland/wayland_surface.hpp"
#include "glad/egl.h"
#include <memory>
@ -26,6 +26,8 @@ class WaylandEGLContext : public OpenGLContext
void swap_buffers();
void swap_interval(int frames);
void make_current();
void shrink();
void regrow();
bool ready();
EGLDisplay egl_display;

View file

@ -1,5 +1,5 @@
#include "slang_preset.hpp"
#include "../external/SPIRV-Cross/spirv.hpp"
#include "external/SPIRV-Cross/spirv.hpp"
#include "slang_helpers.hpp"
#include "slang_preset_ini.hpp"
@ -8,25 +8,15 @@
#include <cstdio>
#include <vector>
#include <cctype>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <future>
#include "../external/SPIRV-Cross/spirv_cross.hpp"
#include "../external/SPIRV-Cross/spirv_glsl.hpp"
#include "external/SPIRV-Cross/spirv_cross.hpp"
#include "external/SPIRV-Cross/spirv_glsl.hpp"
#include "slang_shader.hpp"
using std::string;
using std::to_string;
SlangPreset::SlangPreset()
{
}
SlangPreset::~SlangPreset()
{
}
bool SlangPreset::load_preset_file(string filename)
{
if (!ends_with(filename, ".slangp"))

View file

@ -7,9 +7,6 @@
struct SlangPreset
{
SlangPreset();
~SlangPreset();
void print();
bool load_preset_file(std::string filename);
bool introspect();

View file

@ -2,15 +2,6 @@
#include "slang_helpers.hpp"
#include <fstream>
#include <cstring>
#include <charconv>
IniFile::IniFile()
{
}
IniFile::~IniFile()
{
}
static std::string trim_comments(std::string str)
{

View file

@ -4,8 +4,6 @@
struct IniFile
{
IniFile();
~IniFile();
bool load_file(std::string filename);
std::string get_string(std::string key, std::string default_string);
int get_int(std::string key, int default_int);

View file

@ -7,22 +7,13 @@
#include <sstream>
#include <vector>
#include <fstream>
#include "../external/glslang/glslang/Public/ShaderLang.h"
#include "../external/glslang/SPIRV/GlslangToSpv.h"
#include "../external/glslang/glslang/Public/ResourceLimits.h"
#include "external/glslang/glslang/Public/ShaderLang.h"
#include "external/glslang/SPIRV/GlslangToSpv.h"
#include "external/glslang/glslang/Public/ResourceLimits.h"
using std::string;
using std::vector;
SlangShader::SlangShader()
{
ubo_size = 0;
}
SlangShader::~SlangShader()
{
}
/*
Recursively load shader file and included files into memory, applying
#include and #pragma directives. Will strip all directives except

View file

@ -65,9 +65,6 @@ struct SlangShader
Fragment
};
SlangShader();
~SlangShader();
bool preprocess_shader_file(std::string filename, std::vector<std::string> &lines);
void set_base_path(std::string filename);
bool load_file(std::string new_filename = "");

View file

@ -0,0 +1,164 @@
/* Based on code from Vulkan-Samples:
https://github.com/KhronosGroup/Vulkan-Samples
*/
/* Copyright (c) 2018-2024, Arm Limited and Contributors
* Copyright (c) 2019-2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "vulkan_hpp_wrapper.hpp"
namespace Vulkan {
vk::AccessFlags get_access_flags(vk::ImageLayout layout)
{
switch (layout) {
case vk::ImageLayout::eUndefined:
case vk::ImageLayout::ePresentSrcKHR:
return vk::AccessFlagBits::eNone;
case vk::ImageLayout::ePreinitialized:
return vk::AccessFlagBits::eHostWrite;
case vk::ImageLayout::eColorAttachmentOptimal:
return vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite;
case vk::ImageLayout::eDepthAttachmentOptimal:
return vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
case vk::ImageLayout::eFragmentShadingRateAttachmentOptimalKHR:
return vk::AccessFlagBits::eFragmentShadingRateAttachmentReadKHR;
case vk::ImageLayout::eShaderReadOnlyOptimal:
return vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
case vk::ImageLayout::eTransferSrcOptimal:
return vk::AccessFlagBits::eTransferRead;
case vk::ImageLayout::eTransferDstOptimal:
return vk::AccessFlagBits::eTransferWrite;
case vk::ImageLayout::eGeneral:
return vk::AccessFlagBits::eNone;
default:
return vk::AccessFlagBits::eNone;
}
}
vk::PipelineStageFlags get_pipeline_stage_flags(vk::ImageLayout layout)
{
switch (layout) {
case vk::ImageLayout::eUndefined:
return vk::PipelineStageFlagBits::eTopOfPipe;
case vk::ImageLayout::ePreinitialized:
return vk::PipelineStageFlagBits::eHost;
case vk::ImageLayout::eTransferDstOptimal:
case vk::ImageLayout::eTransferSrcOptimal:
return vk::PipelineStageFlagBits::eTransfer;
case vk::ImageLayout::eColorAttachmentOptimal:
return vk::PipelineStageFlagBits::eColorAttachmentOutput;
case vk::ImageLayout::eDepthAttachmentOptimal:
return vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
case vk::ImageLayout::eFragmentShadingRateAttachmentOptimalKHR:
return vk::PipelineStageFlagBits::eFragmentShadingRateAttachmentKHR;
case vk::ImageLayout::eShaderReadOnlyOptimal:
return vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader;
case vk::ImageLayout::ePresentSrcKHR:
return vk::PipelineStageFlagBits::eBottomOfPipe;
case vk::ImageLayout::eGeneral:
return vk::PipelineStageFlagBits::eNone;
default:
return vk::PipelineStageFlagBits::eNone;
}
}
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::PipelineStageFlags src_stage_mask,
vk::PipelineStageFlags dst_stage_mask,
vk::AccessFlags src_access_mask,
vk::AccessFlags dst_access_mask,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range)
{
auto image_memory_barrier = vk::ImageMemoryBarrier{}
.setSrcAccessMask(src_access_mask)
.setDstAccessMask(dst_access_mask)
.setOldLayout(old_layout)
.setNewLayout(new_layout)
.setImage(image)
.setSubresourceRange(subresource_range);
command_buffer.pipelineBarrier(src_stage_mask,
dst_stage_mask,
{}, {}, {}, image_memory_barrier);
}
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range)
{
vk::PipelineStageFlags src_stage_mask = get_pipeline_stage_flags(old_layout);
vk::PipelineStageFlags dst_stage_mask = get_pipeline_stage_flags(new_layout);
vk::AccessFlags src_access_mask = get_access_flags(old_layout);
vk::AccessFlags dst_access_mask = get_access_flags(new_layout);
image_layout_transition(command_buffer, image, src_stage_mask, dst_stage_mask, src_access_mask, dst_access_mask, old_layout, new_layout, subresource_range);
}
// Fixed sub resource on first mip level and layer
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout)
{
auto subresource_range = vk::ImageSubresourceRange{}
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setBaseMipLevel(0)
.setLevelCount(1)
.setBaseArrayLayer(0)
.setLayerCount(1);
image_layout_transition(command_buffer, image, old_layout, new_layout, subresource_range);
}
void image_layout_transition(vk::CommandBuffer command_buffer,
std::vector<std::pair<vk::Image, vk::ImageSubresourceRange>> const &imagesAndRanges,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout)
{
vk::PipelineStageFlags src_stage_mask = get_pipeline_stage_flags(old_layout);
vk::PipelineStageFlags dst_stage_mask = get_pipeline_stage_flags(new_layout);
vk::AccessFlags src_access_mask = get_access_flags(old_layout);
vk::AccessFlags dst_access_mask = get_access_flags(new_layout);
// Create image barrier objects
std::vector<vk::ImageMemoryBarrier> image_memory_barriers;
for (size_t i = 0; i < imagesAndRanges.size(); i++) {
image_memory_barriers.push_back(vk::ImageMemoryBarrier(
src_access_mask,
dst_access_mask,
old_layout,
new_layout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
imagesAndRanges[i].first,
imagesAndRanges[i].second));
}
// Put barriers inside setup command buffer
command_buffer.pipelineBarrier(src_stage_mask,
dst_stage_mask,
{}, {}, {}, image_memory_barriers);
}
} // namespace Vulkan

View file

@ -0,0 +1,28 @@
#pragma once
#include "vulkan_hpp_wrapper.hpp"
namespace Vulkan {
vk::AccessFlags get_access_flags(vk::ImageLayout layout);
vk::PipelineStageFlags get_pipeline_stage_flags(vk::ImageLayout layout);
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::PipelineStageFlags src_stage_mask,
vk::PipelineStageFlags dst_stage_mask,
vk::AccessFlags src_access_mask,
vk::AccessFlags dst_access_mask,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range);
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range);
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout);
} // namespace Vulkan

View file

@ -1,6 +1,7 @@
#include <cstring>
#include <vector>
#include <string>
#include <optional>
#include "vulkan_context.hpp"
namespace Vulkan
@ -10,6 +11,7 @@ static std::unique_ptr<vk::DynamicLoader> dl;
Context::Context()
{
swapchain = std::make_unique<Swapchain>(*this);
}
Context::~Context()
@ -20,7 +22,6 @@ Context::~Context()
wait_idle();
swapchain.reset();
command_pool.reset();
descriptor_pool.reset();
allocator.destroy();
surface.reset();
wait_idle();
@ -93,45 +94,63 @@ std::vector<std::string> Vulkan::Context::get_device_list()
}
#ifdef VK_USE_PLATFORM_WIN32_KHR
bool Context::init_win32(HINSTANCE hinstance, HWND hwnd, int preferred_device)
bool Context::init_win32()
{
instance = create_instance_preamble(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
if (!instance)
return false;
return init();
}
bool Context::create_win32_surface(HINSTANCE hinstance, HWND hwnd)
{
auto win32_surface_create_info = vk::Win32SurfaceCreateInfoKHR{}
.setHinstance(hinstance)
.setHwnd(hwnd);
surface = instance->createWin32SurfaceKHRUnique(win32_surface_create_info).value;
if (!surface)
auto retval = instance->createWin32SurfaceKHRUnique(win32_surface_create_info);
if (retval.result != vk::Result::eSuccess)
return false;
return init(preferred_device);
surface = std::move(retval.value);
return true;
}
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
bool Context::init_Xlib(Display *dpy, Window xid, int preferred_device)
bool Context::init_Xlib()
{
instance = create_instance_preamble(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
if (!instance)
return false;
platform_name = "xlib";
return init();
}
bool Context::create_Xlib_surface(Display *dpy, Window xid)
{
auto retval = instance->createXlibSurfaceKHRUnique({ {}, dpy, xid });
if (retval.result != vk::Result::eSuccess)
return false;
surface = std::move(retval.value);
return init(preferred_device);
return true;
}
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
bool Context::init_wayland(wl_display *dpy, wl_surface *parent, int initial_width, int initial_height, int preferred_device)
bool Context::init_wayland()
{
instance = create_instance_preamble(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
if (!instance)
return false;
platform_name = "wayland";
return init();
}
bool Context::create_wayland_surface(wl_display *dpy, wl_surface *parent)
{
auto wayland_surface_create_info = vk::WaylandSurfaceCreateInfoKHR{}
.setSurface(parent)
.setDisplay(dpy);
@ -141,35 +160,28 @@ bool Context::init_wayland(wl_display *dpy, wl_surface *parent, int initial_widt
return false;
surface = std::move(new_surface);
return init(preferred_device, initial_width, initial_height);
return true;
}
#endif
bool Context::init(int preferred_device, int initial_width, int initial_height)
bool Context::destroy_surface()
{
init_device(preferred_device);
init_vma();
init_command_pool();
init_descriptor_pool();
create_swapchain(initial_width, initial_height);
wait_idle();
if (swapchain)
swapchain->uncreate();
surface.reset();
return true;
}
bool Context::init_descriptor_pool()
bool Context::init()
{
auto descriptor_pool_size = vk::DescriptorPoolSize{}
.setDescriptorCount(9)
.setType(vk::DescriptorType::eCombinedImageSampler);
auto descriptor_pool_create_info = vk::DescriptorPoolCreateInfo{}
.setPoolSizes(descriptor_pool_size)
.setMaxSets(20)
.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet);
auto retval = device.createDescriptorPoolUnique(descriptor_pool_create_info);
descriptor_pool = std::move(retval.value);
init_device();
init_vma();
init_command_pool();
wait_idle();
return true;
}
@ -192,7 +204,7 @@ static bool find_extension(std::vector<vk::ExtensionProperties> &props, const ch
}) != props.end();
};
static uint32_t find_graphics_queue(vk::PhysicalDevice &device)
static std::optional<uint32_t> find_graphics_queue(vk::PhysicalDevice &device)
{
auto queue_props = device.getQueueFamilyProperties();
for (size_t i = 0; i < queue_props.size(); i++)
@ -203,7 +215,7 @@ static uint32_t find_graphics_queue(vk::PhysicalDevice &device)
}
}
return UINT32_MAX;
return std::nullopt;
}
static bool check_extensions(std::vector<const char *> &required_extensions, vk::PhysicalDevice &device)
@ -217,56 +229,77 @@ static bool check_extensions(std::vector<const char *> &required_extensions, vk:
return true;
};
bool Context::init_device(int preferred_device)
bool Context::init_device()
{
std::vector<const char *> required_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
std::vector<const char *> present_wait_extensions =
{
VK_KHR_PRESENT_ID_EXTENSION_NAME,
VK_KHR_PRESENT_WAIT_EXTENSION_NAME
};
auto device_list = instance->enumeratePhysicalDevices().value;
physical_device = nullptr;
bool device_chosen = false;
physical_device = vk::PhysicalDevice();
if (preferred_device > -1 &&
(size_t)preferred_device < device_list.size() &&
check_extensions(required_extensions, device_list[preferred_device]))
{
physical_device = device_list[preferred_device];
device_chosen = true;
}
if (physical_device == nullptr)
if (!device_chosen)
{
for (auto &device : device_list)
{
if (check_extensions(required_extensions, device))
{
physical_device = device;
device_chosen = true;
break;
}
}
}
auto extension_properties = physical_device.enumerateDeviceExtensionProperties().value;
physical_device.getProperties(&physical_device_props);
if (!device_chosen)
return false;
graphics_queue_family_index = find_graphics_queue(physical_device);
if (graphics_queue_family_index == UINT32_MAX)
if (check_extensions(present_wait_extensions, physical_device))
{
for (auto &ext : present_wait_extensions)
required_extensions.push_back(ext);
have_present_wait = true;
}
else
{
have_present_wait = false;
}
if (auto index = find_graphics_queue(physical_device))
graphics_queue_family_index = *index;
else
return false;
std::vector<float> priorities = { 1.0f };
vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, priorities);
vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions);
vk::PhysicalDevicePresentWaitFeaturesKHR physical_device_present_wait_feature(true);
vk::PhysicalDevicePresentIdFeaturesKHR physical_device_present_id_feature(true);
if (have_present_wait)
{
dci.setPNext(&physical_device_present_wait_feature);
physical_device_present_wait_feature.setPNext(&physical_device_present_id_feature);
}
device = physical_device.createDevice(dci).value;
queue = device.getQueue(graphics_queue_family_index, 0);
auto surface_formats = physical_device.getSurfaceFormatsKHR(surface.get()).value;
if (std::find_if(surface_formats.begin(),
surface_formats.end(),
[](vk::SurfaceFormatKHR &f) {
return (f.format == vk::Format::eB8G8R8A8Unorm);
}) == surface_formats.end())
return false;
return true;
}
@ -286,16 +319,15 @@ bool Context::init_vma()
return true;
}
bool Context::create_swapchain(int width, int height)
bool Context::create_swapchain()
{
wait_idle();
swapchain = std::make_unique<Swapchain>(device, physical_device, queue, surface.get(), command_pool.get());
return swapchain->create(2, width, height);
return swapchain->create();
}
bool Context::recreate_swapchain(int width, int height)
bool Context::recreate_swapchain()
{
return swapchain->recreate(width, height);
return swapchain->recreate();
}
void Context::wait_idle()

View file

@ -8,8 +8,8 @@
#define WINVER 0x599
#endif
#include <cstdint>
#include "vulkan/vulkan_hpp_wrapper.hpp"
#include "../external/VulkanMemoryAllocator-Hpp/include/vk_mem_alloc.hpp"
#include "vulkan_hpp_wrapper.hpp"
#include "external/VulkanMemoryAllocator-Hpp/include/vk_mem_alloc.hpp"
#include "vulkan_swapchain.hpp"
#include <memory>
@ -22,40 +22,47 @@ class Context
Context();
~Context();
#ifdef VK_USE_PLATFORM_XLIB_KHR
bool init_Xlib(Display *dpy, Window xid, int preferred_device = -1);
bool init_Xlib();
bool create_Xlib_surface(Display *dpy, Window xid);
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
bool init_wayland(wl_display *dpy, wl_surface *parent, int width, int height, int preferred_device = -1);
bool init_wayland();
bool create_wayland_surface(wl_display *dpy, wl_surface *parent);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
bool init_win32(HINSTANCE hinstance, HWND hwnd, int preferred_device = -1);
bool init_win32();
bool create_win32_surface(HINSTANCE hinstance, HWND hwnd);
#endif
bool init(int preferred_device = -1, int initial_width = -1, int initial_height = -1);
bool create_swapchain(int width = -1, int height = -1);
bool recreate_swapchain(int width = -1, int height = -1);
bool init();
bool create_swapchain();
bool recreate_swapchain();
bool destroy_surface();
void wait_idle();
vk::CommandBuffer begin_cmd_buffer();
void end_cmd_buffer();
void hard_barrier(vk::CommandBuffer cmd);
static std::vector<std::string> get_device_list();
void set_preferred_device(int device) { preferred_device = device; };
void unset_preferred_device() { preferred_device = -1; };
vma::Allocator allocator;
vk::Device device;
uint32_t graphics_queue_family_index;
vk::Queue queue;
vk::UniqueCommandPool command_pool;
vk::UniqueDescriptorPool descriptor_pool;
std::unique_ptr<Swapchain> swapchain;
vk::UniqueInstance instance;
vk::PhysicalDevice physical_device;
vk::PhysicalDeviceProperties physical_device_props;
vk::UniqueSurfaceKHR surface;
std::string platform_name;
bool have_present_wait;
private:
bool init_vma();
bool init_device(int preferred_device = 0);
bool init_device();
bool init_command_pool();
bool init_descriptor_pool();
int preferred_device;
#ifdef VK_USE_PLATFORM_XLIB_KHR
Display *xlib_display;

View file

@ -0,0 +1,209 @@
#include <cassert>
#include "vulkan_pipeline_image.hpp"
#include "slang_helpers.hpp"
#include "vulkan_common.hpp"
namespace Vulkan
{
PipelineImage::~PipelineImage()
{
destroy();
}
void PipelineImage::init(vk::Device device_, vk::CommandPool command_, vk::Queue queue_, vma::Allocator allocator_)
{
device = device_;
command_pool = command_;
allocator = allocator_;
queue = queue_;
}
void PipelineImage::init(Context *context)
{
device = context->device;
command_pool = context->command_pool.get();
allocator = context->allocator;
queue = context->queue;
}
void PipelineImage::destroy()
{
if (!device || !allocator)
return;
if (image_width != 0 || image_height != 0)
{
framebuffer.reset();
device.destroyImageView(image_view);
device.destroyImageView(mipless_view);
allocator.destroyImage(image, image_allocation);
image_width = image_height = 0;
image_view = nullptr;
image = nullptr;
image_allocation = nullptr;
current_layout = vk::ImageLayout::eUndefined;
}
}
void PipelineImage::generate_mipmaps(vk::CommandBuffer cmd)
{
if (!mipmap)
return;
auto range = [](unsigned int i) { return vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, i, 1, 0, 1); };
auto level = [](unsigned int i) { return vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i, 0, 1); };
auto mipmap_levels = mipmap ? mipmap_levels_for_size(image_width, image_height) : 1;
image_layout_transition(cmd, image, vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eTransferSrcOptimal);
// Transition base layer to readable format.
int base_width = image_width;
int base_height = image_height;
int base_level = 0;
for (; base_level + 1 < mipmap_levels; base_level++)
{
// Transition base layer to readable format.
if (base_level > 0)
{
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eTransferSrcOptimal,
range(base_level));
}
// Transition mipmap layer to writable
image_layout_transition(cmd,
image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
range(base_level + 1));
// Blit base layer to mipmap layer
int mipmap_width = std::max(base_width >> 1, 1);
int mipmap_height = std::max(base_height >> 1, 1);
auto blit = vk::ImageBlit{}
.setSrcOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(base_width, base_height, 1) })
.setDstOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(mipmap_width, mipmap_height, 1) })
.setSrcSubresource(level(base_level))
.setDstSubresource(level(base_level + 1));
base_width = mipmap_width;
base_height = mipmap_height;
cmd.blitImage(image,
vk::ImageLayout::eTransferSrcOptimal,
image,
vk::ImageLayout::eTransferDstOptimal,
blit,
vk::Filter::eLinear);
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
void PipelineImage::barrier(vk::CommandBuffer cmd)
{
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eBottomOfPipe,
vk::PipelineStageFlagBits::eFragmentShader,
{}, {}, {}, {});
}
void PipelineImage::clear(vk::CommandBuffer cmd)
{
vk::ImageSubresourceRange subresource_range(vk::ImageAspectFlagBits::eColor, 0, VK_REMAINING_MIP_LEVELS, 0, 1);
image_layout_transition(cmd,
image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
subresource_range);
vk::ClearColorValue color{};
color.setFloat32({ 0.0f, 0.0f, 0.0f, 1.0f });
cmd.clearColorImage(image,
vk::ImageLayout::eTransferDstOptimal,
color,
subresource_range);
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
subresource_range);
current_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
}
void PipelineImage::create(int width, int height, vk::Format fmt, vk::RenderPass renderpass, bool mipmap)
{
assert(width + height);
assert(device && allocator);
this->mipmap = mipmap;
int mipmap_levels = mipmap ? mipmap_levels_for_size(width, height): 1;
format = fmt;
auto allocation_create_info = vma::AllocationCreateInfo{}
.setUsage(vma::MemoryUsage::eAuto);
auto image_create_info = vk::ImageCreateInfo{}
.setUsage(vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled)
.setImageType(vk::ImageType::e2D)
.setExtent(vk::Extent3D(width, height, 1))
.setMipLevels(mipmap_levels)
.setArrayLayers(1)
.setFormat(format)
.setInitialLayout(vk::ImageLayout::eUndefined)
.setSamples(vk::SampleCountFlagBits::e1)
.setSharingMode(vk::SharingMode::eExclusive);
std::tie(image, image_allocation) = allocator.createImage(image_create_info, allocation_create_info).value;
auto subresource_range = vk::ImageSubresourceRange{}
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setBaseArrayLayer(0)
.setBaseMipLevel(0)
.setLayerCount(1)
.setLevelCount(mipmap_levels);
auto image_view_create_info = vk::ImageViewCreateInfo{}
.setImage(image)
.setViewType(vk::ImageViewType::e2D)
.setFormat(format)
.setComponents(vk::ComponentMapping())
.setSubresourceRange(subresource_range);
image_view = device.createImageView(image_view_create_info).value;
image_view_create_info.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
mipless_view = device.createImageView(image_view_create_info).value;
image_width = width;
image_height = height;
auto framebuffer_create_info = vk::FramebufferCreateInfo{}
.setAttachments(mipless_view)
.setWidth(width)
.setHeight(height)
.setRenderPass(renderpass)
.setLayers(1);
framebuffer = device.createFramebufferUnique(framebuffer_create_info).value;
}
} // namespace Vulkan

View file

@ -6,7 +6,7 @@ namespace Vulkan
struct PipelineImage
{
PipelineImage();
PipelineImage() = default;
void init(vk::Device device, vk::CommandPool command, vk::Queue queue, vma::Allocator allocator);
void init(Vulkan::Context *context);
~PipelineImage();

View file

@ -1,7 +1,10 @@
#include <cassert>
#include "vulkan_shader_chain.hpp"
#include "slang_helpers.hpp"
#include "stb_image.h"
#include "vulkan/vulkan_enums.hpp"
#include "vulkan_common.hpp"
namespace Vulkan
{
@ -24,6 +27,7 @@ ShaderChain::~ShaderChain()
{
if (context && context->device)
{
context->wait_idle();
if (vertex_buffer)
context->allocator.destroyBuffer(vertex_buffer, vertex_buffer_allocation);
vertex_buffer = nullptr;
@ -50,16 +54,13 @@ void ShaderChain::construct_buffer_objects()
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
std::string block;
switch (uniform.block)
{
case SlangShader::Uniform::UBO:
location = &ubo_memory[uniform.offset];
block = "uniform";
break;
case SlangShader::Uniform::PushConstant:
location = &pipeline.push_constants[uniform.offset];
block = "push constant";
break;
}
@ -416,6 +417,7 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
{
auto &pipe = *pipelines[i];
auto &frame = pipe.frame[current_frame_index];
bool is_last_pass = (i == pipelines.size() - 1);
update_descriptor_set(cmd, i, current_frame_index);
@ -428,7 +430,7 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
.setRenderArea(vk::Rect2D({}, vk::Extent2D(frame.image.image_width, frame.image.image_height)))
.setClearValues(value);
if (i == pipelines.size() - 1)
if (is_last_pass)
context->swapchain->begin_render_pass();
else
cmd.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
@ -439,52 +441,36 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
if (pipe.push_constants.size() > 0)
cmd.pushConstants(pipe.pipeline_layout.get(), vk::ShaderStageFlagBits::eAllGraphics, 0, pipe.push_constants.size(), pipe.push_constants.data());
if (i < pipelines.size() - 1)
{
cmd.setViewport(0, vk::Viewport(0, 0, pipe.destination_width, pipe.destination_height, 0.0f, 1.0f));
cmd.setScissor(0, vk::Rect2D({}, vk::Extent2D(pipe.destination_width, pipe.destination_height)));
}
else
if (is_last_pass)
{
cmd.setViewport(0, vk::Viewport(viewport_x, viewport_y, viewport_width, viewport_height, 0.0f, 1.0f));
cmd.setScissor(0, vk::Rect2D(vk::Offset2D(viewport_x, viewport_y), vk::Extent2D(viewport_width, viewport_height)));
}
cmd.draw(3, 1, 0, 0);
if (i < pipelines.size() - 1)
{
cmd.endRenderPass();
}
else
{
context->swapchain->end_render_pass();
cmd.setViewport(0, vk::Viewport(0, 0, pipe.destination_width, pipe.destination_height, 0.0f, 1.0f));
cmd.setScissor(0, vk::Rect2D({}, vk::Extent2D(pipe.destination_width, pipe.destination_height)));
}
cmd.draw(3, 1, 0, 0);
if (is_last_pass)
context->swapchain->end_render_pass();
else
cmd.endRenderPass();
frame.image.barrier(cmd);
if (i < pipelines.size() - 1)
if (!is_last_pass)
frame.image.generate_mipmaps(cmd);
if (preset->last_pass_uses_feedback && i == pipelines.size() - 1)
if (preset->last_pass_uses_feedback && is_last_pass)
{
std::array<vk::ImageMemoryBarrier, 2> image_memory_barrier{};
image_memory_barrier[0]
.setImage(frame.image.image)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
image_memory_barrier[1]
.setImage(context->swapchain->get_image())
.setOldLayout(vk::ImageLayout::ePresentSrcKHR)
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, image_memory_barrier);
image_layout_transition(cmd, frame.image.image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal);
image_layout_transition(cmd, context->swapchain->get_image(),
vk::ImageLayout::ePresentSrcKHR,
vk::ImageLayout::eTransferSrcOptimal);
auto image_blit = vk::ImageBlit{}
.setSrcOffsets({ vk::Offset3D(viewport_x, viewport_y, 0), vk::Offset3D(viewport_x + viewport_width, viewport_y + viewport_height, 1) })
@ -494,22 +480,12 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
cmd.blitImage(context->swapchain->get_image(), vk::ImageLayout::eTransferSrcOptimal, frame.image.image, vk::ImageLayout::eTransferDstOptimal, image_blit, vk::Filter::eNearest);
image_memory_barrier[0]
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eShaderRead)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
image_memory_barrier[1]
.setOldLayout(vk::ImageLayout::eTransferSrcOptimal)
.setNewLayout(vk::ImageLayout::ePresentSrcKHR)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eMemoryRead)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eAllGraphics,
{}, {}, {}, image_memory_barrier);
image_layout_transition(cmd, frame.image.image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal);
image_layout_transition(cmd, context->swapchain->get_image(),
vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::ePresentSrcKHR);
frame.image.current_layout = vk::ImageLayout::eTransferDstOptimal;
}
@ -525,33 +501,34 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
void ShaderChain::upload_original(vk::CommandBuffer cmd, uint8_t *data, int width, int height, int stride, vk::Format format)
{
std::unique_ptr<Texture> texture;
auto create_texture = [&]() {
texture->create(width,
height,
format,
wrap_mode_from_string(pipelines[0]->shader->wrap_mode),
pipelines[0]->shader->filter_linear,
pipelines[0]->shader->mipmap_input);
};
bool create_texture = false;
if (original.size() > original_history_size)
{
texture = std::move(original.back());
original.pop_back();
if (texture->image_width != width || texture->image_height != height || texture->format != format)
{
texture->destroy();
create_texture();
create_texture = true;
}
}
else
{
texture = std::make_unique<Texture>();
texture->init(context);
create_texture();
create_texture = true;
}
if (create_texture)
texture->create(width,
height,
format,
wrap_mode_from_string(pipelines[0]->shader->wrap_mode),
pipelines[0]->shader->filter_linear,
pipelines[0]->shader->mipmap_input);
if (cmd)
texture->from_buffer(cmd, data, width, height, stride);
else

View file

@ -48,6 +48,7 @@ SimpleOutput::~SimpleOutput()
context->wait_idle();
textures.clear();
descriptors.clear();
descriptor_pool.reset();
device.destroySampler(linear_sampler);
device.destroySampler(nearest_sampler);
}
@ -55,11 +56,17 @@ SimpleOutput::~SimpleOutput()
void SimpleOutput::create_objects()
{
descriptors.clear();
descriptor_pool.reset();
vk::DescriptorPoolSize descriptor_pool_size(vk::DescriptorType::eCombinedImageSampler, 20);
vk::DescriptorPoolCreateInfo dpci(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, 20,
descriptor_pool_size);
descriptor_pool = device.createDescriptorPoolUnique(dpci).value;
for (int i = 0; i < queue_size; i++)
{
vk::DescriptorSetAllocateInfo dsai{};
dsai
.setDescriptorPool(context->descriptor_pool.get())
auto dsai = vk::DescriptorSetAllocateInfo{}
.setDescriptorPool(descriptor_pool.get())
.setDescriptorSetCount(1)
.setSetLayouts(descriptor_set_layout.get());
auto descriptor = device.allocateDescriptorSetsUnique(dsai).value;

View file

@ -25,6 +25,7 @@ class SimpleOutput
vk::Device device;
Vulkan::Swapchain *swapchain;
vk::UniqueDescriptorPool descriptor_pool;
vk::UniqueDescriptorSetLayout descriptor_set_layout;
vk::UniquePipelineLayout pipeline_layout;
vk::UniquePipeline pipeline;

View file

@ -1,6 +1,4 @@
#include "vulkan_slang_pipeline.hpp"
#include "slang_helpers.hpp"
#include <unordered_map>
namespace Vulkan
{
@ -65,18 +63,6 @@ vk::SamplerAddressMode wrap_mode_from_string(std::string s)
return vk::SamplerAddressMode::eClampToBorder;
}
SlangPipeline::SlangPipeline()
{
device = nullptr;
shader = nullptr;
uniform_buffer = nullptr;
uniform_buffer_allocation = nullptr;
source_width = 0;
source_height = 0;
destination_width = 0;
destination_height = 0;
}
void SlangPipeline::init(Context *context_, SlangShader *shader_)
{
this->context = context_;

View file

@ -1,5 +1,5 @@
#pragma once
#include "vulkan/vulkan_hpp_wrapper.hpp"
#include "vulkan_hpp_wrapper.hpp"
#include "slang_shader.hpp"
#include "vulkan_context.hpp"
#include "vulkan_pipeline_image.hpp"
@ -10,7 +10,7 @@ namespace Vulkan
class SlangPipeline
{
public:
SlangPipeline();
SlangPipeline() = default;
void init(Context *context_, SlangShader *shader_);
~SlangPipeline();

View file

@ -1,17 +1,17 @@
#include "vulkan_swapchain.hpp"
#include "vulkan_context.hpp"
namespace Vulkan
{
Swapchain::Swapchain(vk::Device device_, vk::PhysicalDevice physical_device_, vk::Queue queue_, vk::SurfaceKHR surface_, vk::CommandPool command_pool_)
: surface(surface_),
command_pool(command_pool_),
physical_device(physical_device_),
queue(queue_)
Swapchain::Swapchain(Context &context_)
: context(context_),
device(context.device),
queue(context.queue),
physical_device(context.physical_device),
command_pool(context.command_pool.get()),
surface(context.surface.get())
{
device = device_;
create_render_pass();
end_render_pass_function = nullptr;
}
Swapchain::~Swapchain()
@ -20,7 +20,12 @@ Swapchain::~Swapchain()
void Swapchain::set_vsync(bool new_setting)
{
vsync = new_setting;
if (vsync != new_setting)
{
vsync = new_setting;
if (swapchain_object)
recreate();
}
}
void Swapchain::on_render_pass_end(std::function<void ()> function)
@ -73,15 +78,14 @@ void Swapchain::create_render_pass()
render_pass = device.createRenderPassUnique(render_pass_create_info).value;
}
bool Swapchain::recreate(int new_width, int new_height)
bool Swapchain::recreate()
{
if (swapchain_object)
{
device.waitIdle();
wait_on_frames();
}
return create(num_swapchain_images, new_width, new_height);
return create();
}
vk::Image Swapchain::get_image()
@ -101,9 +105,13 @@ static bool vector_find(std::vector<T> haystack, T&& needle)
vk::PresentModeKHR Swapchain::get_present_mode() {
auto present_mode = vk::PresentModeKHR::eFifo;
if (!vsync) {
if (context.platform_name == "wayland")
{
if (supports_mailbox)
present_mode = vk::PresentModeKHR::eMailbox;
}
if (!vsync) {
if (supports_immediate)
present_mode = vk::PresentModeKHR::eImmediate;
}
@ -127,24 +135,44 @@ bool Swapchain::check_and_resize(int width, int height)
if (extents.width != (uint32_t)width || extents.height != (uint32_t)height)
{
recreate(width, height);
set_desired_size(width, height);
recreate();
return true;
}
return false;
}
bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, int new_height)
bool Swapchain::uncreate()
{
frames.clear();
image_data.clear();
swapchain_object.reset();
return true;
}
bool Swapchain::create()
{
if (!render_pass)
create_render_pass();
frames.clear();
image_data.clear();
auto present_modes = physical_device.getSurfacePresentModesKHR(surface).value;
for (auto &mode : present_modes)
{
if (mode == vk::PresentModeKHR::eMailbox)
supports_mailbox = true;
}
auto surface_capabilities = physical_device.getSurfaceCapabilitiesKHR(surface).value;
if (surface_capabilities.minImageCount > desired_num_swapchain_images)
if (desired_latency == - 1 || (int)surface_capabilities.minImageCount > desired_latency)
num_swapchain_images = surface_capabilities.minImageCount;
else
num_swapchain_images = desired_num_swapchain_images;
num_swapchain_images = desired_latency;
// If extents aren't reported (Wayland), we have to rely on Wayland to report
// the size, so keep current extent.
@ -162,13 +190,21 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
}
}
if (new_width > 0 && new_height > 0)
if (desired_width > 0 && desired_height > 0)
{
// No buffer is allocated for surface yet
extents.width = new_width;
extents.height = new_height;
extents.width = desired_width;
extents.height = desired_height;
}
else if (extents.width < 1 || extents.height < 1)
extents.width = std::clamp(extents.width,
surface_capabilities.minImageExtent.width,
surface_capabilities.maxImageExtent.width);
extents.height = std::clamp(extents.height,
surface_capabilities.minImageExtent.height,
surface_capabilities.maxImageExtent.height);
if (extents.width < 1 || extents.height < 1)
{
// Surface is likely hidden
printf("Extents too small.\n");
@ -176,23 +212,6 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
return false;
}
if (extents.width > surface_capabilities.maxImageExtent.width)
extents.width = surface_capabilities.maxImageExtent.width;
if (extents.height > surface_capabilities.maxImageExtent.height)
extents.height = surface_capabilities.maxImageExtent.height;
if (extents.width < surface_capabilities.minImageExtent.width)
extents.width = surface_capabilities.minImageExtent.width;
if (extents.height < surface_capabilities.minImageExtent.height)
extents.height = surface_capabilities.minImageExtent.height;
auto present_modes = physical_device.getSurfacePresentModesKHR(surface).value;
supports_mailbox = vector_find(present_modes, vk::PresentModeKHR::eMailbox);
supports_immediate = vector_find(present_modes, vk::PresentModeKHR::eImmediate);
supports_relaxed = vector_find(present_modes, vk::PresentModeKHR::eFifoRelaxed);
auto swapchain_maintenance_info = vk::SwapchainPresentModesCreateInfoEXT{}
.setPresentModes(present_modes);
auto swapchain_create_info = vk::SwapchainCreateInfoKHR{}
.setMinImageCount(num_swapchain_images)
.setImageFormat(vk::Format::eB8G8R8A8Unorm)
@ -206,8 +225,7 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
.setSurface(surface)
.setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity)
.setImageArrayLayers(1)
.setQueueFamilyIndices(graphics_queue_index)
.setPNext(&swapchain_maintenance_info);
.setQueueFamilyIndices(graphics_queue_index);
swapchain_object.reset();
auto resval = device.createSwapchainKHRUnique(swapchain_create_info);
@ -267,8 +285,6 @@ bool Swapchain::create_resources()
.setLayers(1)
.setRenderPass(render_pass.get());
image.framebuffer = device.createFramebufferUnique(framebuffer_create_info).value;
image.fence = device.createFenceUnique(fence_create_info).value;
}
current_swapchain_image = 0;
@ -295,7 +311,7 @@ bool Swapchain::begin_frame()
}
vk::ResultValue<uint32_t> result_value(vk::Result::eSuccess, 0);
result_value = device.acquireNextImageKHR(swapchain_object.get(), UINT64_MAX, frame.acquire.get());
result_value = device.acquireNextImageKHR(swapchain_object.get(), 33333333, frame.acquire.get());
if (result_value.result == vk::Result::eErrorOutOfDateKHR ||
result_value.result == vk::Result::eSuboptimalKHR)
@ -348,15 +364,13 @@ bool Swapchain::swap()
.setSwapchains(swapchain_object.get())
.setImageIndices(current_swapchain_image);
vk::SwapchainPresentModeInfoEXT present_mode_info;
auto present_mode = get_present_mode();
present_mode_info.setPresentModes(present_mode);
present_info.setPNext(&present_mode_info);
auto &present_fence = image_data[current_swapchain_image].fence.get();
device.resetFences(present_fence);
vk::SwapchainPresentFenceInfoEXT present_fence_info(present_fence);
present_mode_info.setPNext(&present_fence_info);
vk::PresentIdKHR present_id;
if (context.have_present_wait)
{
presentation_id++;
present_id.setPresentIds(presentation_id);
present_info.setPNext(&present_id);
}
vk::Result result = queue.presentKHR(present_info);
if (result == vk::Result::eErrorOutOfDateKHR)
@ -414,18 +428,6 @@ void Swapchain::end_render_pass()
get_cmd().endRenderPass();
}
bool Swapchain::wait_on_frame(int frame_num)
{
auto result = device.waitForFences({ image_data[frame_num].fence.get() }, true, 33000000);
return (result == vk::Result::eSuccess);
}
void Swapchain::wait_on_frames()
{
for (auto i = 0; i < image_data.size(); i++)
wait_on_frame(i);
}
vk::Extent2D Swapchain::get_extents()
{
return extents;
@ -436,4 +438,12 @@ vk::RenderPass &Swapchain::get_render_pass()
return render_pass.get();
}
void Swapchain::present_wait()
{
if (context.have_present_wait && context.platform_name != "wayland")
{
device.waitForPresentKHR(swapchain_object.get(), presentation_id, 16666666);
}
}
} // namespace Vulkan

View file

@ -1,32 +1,35 @@
#pragma once
#include "vulkan/vulkan_hpp_wrapper.hpp"
#include "vulkan_hpp_wrapper.hpp"
#include <functional>
namespace Vulkan
{
class Context;
class Swapchain
{
public:
Swapchain(vk::Device device,
vk::PhysicalDevice physical_device,
vk::Queue queue,
vk::SurfaceKHR surface,
vk::CommandPool command_pool);
Swapchain(Context &);
~Swapchain();
bool create(unsigned int num_frames, int width = -1, int height = -1);
bool recreate(int width = -1, int height = -1);
bool create();
bool uncreate();
bool recreate();
bool create_resources();
bool check_and_resize(int width = -1, int height = -1);
Swapchain &set_desired_size(int width, int height) { desired_width = width; desired_height = height; return *this; }
void unset_desired_size() { desired_width = -1; desired_height = -1; }
Swapchain &set_desired_latency(int latency) { desired_latency = latency; return *this; }
void unset_desired_latency() { desired_latency = -1; }
bool begin_frame();
void begin_render_pass();
void end_render_pass();
bool wait_on_frame(int frame_num);
bool end_frame();
void end_frame_without_swap();
bool swap();
void wait_on_frames();
void present_wait();
void set_vsync(bool on);
void on_render_pass_end(std::function<void()> function);
int get_num_frames() { return num_swapchain_images; }
@ -39,7 +42,7 @@ class Swapchain
vk::RenderPass &get_render_pass();
private:
std::function<void()> end_render_pass_function;
std::function<void()> end_render_pass_function = nullptr;
void create_render_pass();
struct Frame
@ -53,7 +56,6 @@ class Swapchain
struct ImageData
{
vk::Image image;
vk::UniqueFence fence;
vk::UniqueImageView image_view;
vk::UniqueFramebuffer framebuffer;
};
@ -66,6 +68,10 @@ class Swapchain
unsigned int current_frame = 0;
unsigned int current_swapchain_image = 0;
unsigned int num_swapchain_images = 0;
int desired_width = -1;
int desired_height = -1;
int desired_latency = -1;
uint64_t presentation_id = 0;
bool vsync = true;
bool supports_immediate = false;
bool supports_mailbox = false;
@ -73,11 +79,12 @@ class Swapchain
std::vector<Frame> frames;
std::vector<ImageData> image_data;
vk::Device device;
vk::SurfaceKHR surface;
vk::CommandPool command_pool;
vk::PhysicalDevice physical_device;
vk::Queue queue;
Vulkan::Context &context;
vk::Device &device;
vk::Queue &queue;
vk::PhysicalDevice &physical_device;
vk::CommandPool &command_pool;
vk::SurfaceKHR &surface;
};
} // namespace Vulkan

View file

@ -1,24 +1,12 @@
#include <cassert>
#include "vulkan_texture.hpp"
#include "vulkan/vulkan_enums.hpp"
#include "slang_helpers.hpp"
#include "vulkan_common.hpp"
namespace Vulkan
{
Texture::Texture()
{
image_width = 0;
image_height = 0;
buffer_size = 0;
device = nullptr;
command_pool = nullptr;
allocator = nullptr;
queue = nullptr;
buffer = nullptr;
image = nullptr;
sampler = nullptr;
}
Texture::~Texture()
{
destroy();
@ -101,20 +89,13 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
allocator.unmapMemory(buffer_allocation);
allocator.flushAllocation(buffer_allocation, 0, width * height * pixel_size);
auto srr = [](unsigned int i) { return vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, i, 1, 0, 1); };
auto srl = [](unsigned int i) { return vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i, 0, 1); };
auto range = [](unsigned int i) { return vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, i, 1, 0, 1); };
auto level = [](unsigned int i) { return vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i, 0, 1); };
auto barrier = vk::ImageMemoryBarrier{}
.setImage(image)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eShaderRead)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setSubresourceRange(srr(0));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
range(0));
auto buffer_image_copy = vk::BufferImageCopy{}
.setBufferOffset(0)
@ -122,7 +103,7 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
.setBufferImageHeight(height)
.setImageExtent(vk::Extent3D(width, height, 1))
.setImageOffset(vk::Offset3D(0, 0, 0))
.setImageSubresource(srl(0));
.setImageSubresource(level(0));
cmd.copyBufferToImage(this->buffer, image, vk::ImageLayout::eTransferDstOptimal, buffer_image_copy);
auto mipmap_levels = mipmap ? mipmap_levels_for_size(image_width, image_height) : 1;
@ -133,30 +114,16 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
for (; base_level + 1 < mipmap_levels; base_level++)
{
// Transition base layer to readable format.
barrier
.setImage(image)
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setSubresourceRange(srr(base_level));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eTransferSrcOptimal,
range(base_level));
// Transition mipmap layer to writable
barrier
.setImage(image)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferRead)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setSubresourceRange(srr(base_level + 1));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
range(base_level + 1));
// Blit base layer to mipmap layer
int mipmap_width = base_width >> 1;
@ -169,8 +136,8 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
auto blit = vk::ImageBlit{}
.setSrcOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(base_width, base_height, 1) })
.setDstOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(mipmap_width, mipmap_height, 1)})
.setSrcSubresource(srl(base_level))
.setDstSubresource(srl(base_level + 1));
.setSrcSubresource(level(base_level))
.setDstSubresource(level(base_level + 1));
base_width = mipmap_width;
base_height = mipmap_height;
@ -178,29 +145,17 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
cmd.blitImage(image, vk::ImageLayout::eTransferSrcOptimal, image, vk::ImageLayout::eTransferDstOptimal, blit, vk::Filter::eLinear);
// Transition base layer to shader readable
barrier
.setOldLayout(vk::ImageLayout::eTransferSrcOptimal)
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eShaderRead)
.setSubresourceRange(srr(base_level));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
// Transition final layer to shader readable
barrier
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eShaderRead)
.setSubresourceRange(srr(base_level));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
void Texture::from_buffer(uint8_t *buffer, int width, int height, int byte_stride)

View file

@ -7,7 +7,7 @@ namespace Vulkan
struct Texture
{
Texture();
Texture() = default;
void init(vk::Device device, vk::CommandPool command, vk::Queue queue, vma::Allocator allocator);
void init(Context *context);
~Texture();

View file

@ -23,15 +23,15 @@ static void wl_global(void *data,
auto wl = (WaylandSurface *)data;
if (!strcmp(interface, "wl_compositor"))
wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3);
wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, version);
else if (!strcmp(interface, "wl_subcompositor"))
wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, version);
else if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name))
wl->idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
wl->idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, version);
else if (!strcmp(interface, wp_viewporter_interface.name))
wl->viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1);
wl->viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, version);
else if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name))
wl->fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1);
wl->fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, version);
}
static void wl_global_remove(void *data,
@ -161,10 +161,37 @@ std::tuple<int, int> WaylandSurface::get_size_for_metrics(Metrics m)
return { round(m.width * actual_scale), round(m.height * actual_scale) };
}
void WaylandSurface::shrink()
{
if (!viewport)
viewport = wp_viewporter_get_viewport(viewporter, child);
wp_viewport_set_source(viewport,
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
wl_fixed_from_int(-1), wl_fixed_from_int(-1));
wp_viewport_set_destination(viewport, 2, 2);
wl_surface_commit(child);
wl_surface_commit(parent);
}
void WaylandSurface::regrow()
{
if (!viewport)
viewport = wp_viewporter_get_viewport(viewporter, child);
wp_viewport_set_source(viewport,
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
wl_fixed_from_int(-1), wl_fixed_from_int(-1));
wp_viewport_set_destination(viewport, metrics.width, metrics.height);
wl_surface_commit(child);
wl_surface_commit(parent);
}
void WaylandSurface::resize(Metrics m)
{
metrics = m;
auto [w, h] = get_size();
wl_subsurface_set_position(subsurface, m.x, m.y);

View file

@ -22,6 +22,8 @@ class WaylandSurface
bool attach(wl_display *display, wl_surface *surface, Metrics source_metrics);
void resize(Metrics new_metrics);
void shrink();
void regrow();
std::tuple<int, int> get_size();
std::tuple<int, int> get_size_for_metrics(Metrics m);

View file

@ -1,4 +1,4 @@
// Formatting library for C++ - dynamic format arguments
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
@ -8,11 +8,13 @@
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#ifndef FMT_MODULE
# include <functional> // std::reference_wrapper
# include <memory> // std::unique_ptr
# include <vector>
#endif
#include "core.h"
#include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE
@ -22,20 +24,24 @@ template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
template <typename T> auto unwrap(const T& v) -> const T& { return v; }
template <typename T>
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v);
}
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// 2022 (v17.10.0).
//
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So node is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
class dynamic_arg_list {
template <typename T> struct typed_node : node<> {
T value;
@ -50,7 +56,7 @@ class dynamic_arg_list {
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
@ -61,14 +67,10 @@ class dynamic_arg_list {
} // namespace detail
/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
* A dynamic list of formatting arguments with storage.
*
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context>
class dynamic_format_arg_store
@ -110,14 +112,14 @@ class dynamic_format_arg_store
friend class basic_format_args<Context>;
unsigned long long get_types() const {
auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
const basic_format_arg<Context>* data() const {
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
@ -146,22 +148,20 @@ class dynamic_format_arg_store
constexpr dynamic_format_arg_store() = default;
/**
\rst
Adds an argument into the dynamic store for later passing to a formatting
function.
Note that custom types and string types (but not string views) are copied
into the store dynamically allocating memory if necessary.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
* Adds an argument into the dynamic store for later passing to a formatting
* function.
*
* Note that custom types and string types (but not string views) are copied
* into the store dynamically allocating memory if necessary.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* store.push_back(42);
* store.push_back("abc");
* store.push_back(1.5f);
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
@ -170,20 +170,18 @@ class dynamic_format_arg_store
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band));
band[9] = 'c'; // Changing str affects the output.
std::string result = fmt::vformat("{}", store);
// result == "Rolling Scones"
\endrst
*/
* Adds a reference to the argument into the dynamic store for later passing
* to a formatting function.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* char band[] = "Rolling Stones";
* store.push_back(std::cref(band));
* band[9] = 'c'; // Changing str affects the output.
* std::string result = fmt::vformat("{}", store);
* // result == "Rolling Scones"
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
@ -192,10 +190,10 @@ class dynamic_format_arg_store
}
/**
Adds named argument into the dynamic store for later passing to a formatting
function. ``std::reference_wrapper`` is supported to avoid copying of the
argument. The name is always copied into the store.
*/
* Adds named argument into the dynamic store for later passing to a
* formatting function. `std::reference_wrapper` is supported to avoid
* copying of the argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
@ -208,19 +206,15 @@ class dynamic_format_arg_store
}
}
/** Erase all elements from the store */
/// Erase all elements from the store.
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list();
}
/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
/// Reserves space to store at least `new_cap` arguments including
/// `new_cap_named` named arguments.
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments");

3077
external/fmt/include/fmt/base.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -10,15 +10,8 @@
#include "format.h"
// __declspec(deprecated) is broken in some MSVC versions.
#if FMT_MSC_VER
# define FMT_DEPRECATED_NONMSVC
#else
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
#endif
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
@ -210,7 +203,7 @@ struct rgb {
uint8_t b;
};
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@ -232,22 +225,21 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace detail
FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */
/// A text style consisting of foreground and background colors and emphasis.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), set_background_color(), ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
@ -256,7 +248,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
@ -265,39 +257,29 @@ class text_style {
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
-> text_style {
return lhs |= rhs;
}
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
const text_style& rhs) {
return and_assign(rhs);
}
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
operator&(text_style lhs, const text_style& rhs) {
return lhs.and_assign(rhs);
}
FMT_CONSTEXPR bool has_foreground() const noexcept {
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const noexcept {
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const noexcept {
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR detail::color_type get_background() const noexcept {
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
@ -315,36 +297,11 @@ class text_style {
}
}
// DEPRECATED!
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
-> text_style;
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR_DECL text_style
fg(detail::color_type foreground) noexcept;
friend FMT_CONSTEXPR_DECL text_style
bg(detail::color_type background) noexcept;
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
-> text_style;
detail::color_type foreground_color;
detail::color_type background_color;
@ -353,21 +310,24 @@ class text_style {
emphasis ems;
};
/** Creates a text style from the foreground (text) color. */
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
/// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
/// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return text_style(false, background);
}
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
-> text_style {
return text_style(lhs) | rhs;
}
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@ -429,9 +389,9 @@ template <typename Char> struct ansi_color_escape {
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
return buffer + std::char_traits<Char>::length(buffer);
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
}
private:
@ -445,62 +405,45 @@ template <typename Char> struct ansi_color_escape {
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
-> bool {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) noexcept {
FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) noexcept {
FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em);
}
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
int result = std::fputs(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
int result = std::fputws(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg {
template <typename T> struct styled_arg : detail::view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
void vformat_to(
buffer<Char>& buf, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffered_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
@ -521,118 +464,94 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
if (has_style) detail::reset_color<Char>(buf);
}
FMT_END_DETAIL_NAMESPACE
} // namespace detail
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
if (detail::is_utf8()) {
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
} else {
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
* Formats a string and prints it to the specified file stream using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
vprint(f, ts, format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
template <typename... T>
void print(FILE* f, const text_style& ts, format_string<T...> fmt,
T&&... args) {
vprint(f, ts, fmt, fmt::make_format_args(args...));
}
/**
\rst
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
* Formats a string and prints it to stdout using ANSI escape sequences to
* specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
template <typename... T>
void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
inline auto vformat(const text_style& ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return fmt::vformat(ts, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
* Formats arguments and returns the result as a string using ANSI escape
* sequences to specify text formatting.
*
* **Example**:
*
* ```
* #include <fmt/color.h>
* std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
* "The answer is {}", 42);
* ```
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
template <typename... T>
inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
-> std::string {
return fmt::vformat(ts, fmt, fmt::make_format_args(args...));
}
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
format_args args) -> OutputIt {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
* Formats arguments with the given text style, writes the result to the output
* iterator `out` and returns the iterator past the end of the output range.
*
* **Example**:
*
* std::vector<char> out;
* fmt::format_to(std::back_inserter(out),
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, const text_style& ts,
format_string<T...> fmt, T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt, fmt::make_format_args(args...));
}
template <typename T, typename Char>
@ -672,15 +591,14 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
};
/**
\rst
Returns an argument that will be formatted using ANSI escape sequences,
to be used in a formatting function.
**Example**::
fmt::print("Elapsed time: {s:.2f} seconds",
fmt::styled(1.23, fmt::fg(fmt::color::green) | fmt::bg(fmt::color::blue)));
\endrst
* Returns an argument that will be formatted using ANSI escape sequences,
* to be used in a formatting function.
*
* **Example**:
*
* fmt::print("Elapsed time: {0:.2f} seconds",
* fmt::styled(1.23, fmt::fg(fmt::color::green) |
* fmt::bg(fmt::color::blue)));
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
@ -688,7 +606,7 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts)
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
}
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View file

@ -8,122 +8,46 @@
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
FMT_EXPORT class compiled_string {};
namespace detail {
template <typename T, typename InputIt>
FMT_CONSTEXPR inline auto copy(InputIt begin, InputIt end, counting_iterator it)
-> counting_iterator {
return it + (end - begin);
}
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
* `constexpr if` compiler support.
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* std::string s = fmt::format(FMT_COMPILE("{}"), 42);
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
@ -135,7 +59,7 @@ struct udl_compiled_string : compiled_string {
#endif
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
auto first(const T& value, const Tail&...) -> const T& {
return value;
}
@ -196,7 +120,8 @@ template <typename Char> struct code_unit {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
*out++ = value;
return out;
}
};
@ -220,7 +145,12 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
return write<Char>(out, get_arg_checked<T, N>(args...));
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
}
return write<Char>(out, arg);
}
};
@ -308,13 +238,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
@ -331,38 +260,35 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id;
};
constexpr int manual_indexing_id = -1;
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
}
template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id;
constexpr int operator()() {
constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int operator()(int id) {
constexpr int on_index(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
constexpr int on_name(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
};
template <typename Char> struct parse_arg_id_result {
@ -388,43 +314,48 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
} else if constexpr (c == ':') {
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
} else {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
if constexpr (result.end >= str.size() || str[result.end] != '}') {
FMT_THROW(format_error("expected '}'"));
return 0;
} else {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
fmt);
}
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(
format_str);
POS + 1, ID, next_id>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@ -440,60 +371,55 @@ constexpr auto compile_format_string(S format_str) {
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
fmt);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) {
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
return result;
}
}
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
@ -558,35 +484,36 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) {
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
format_str, std::forward<Args>(args)...);
return {it.base(), it.count()};
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) {
return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
return fmt::format_to(detail::counting_iterator(), fmt, args...).count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) {
void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), format_str, args...);
fmt::format_to(std::back_inserter(buffer), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
using char_t = remove_cvref_t<decltype(Str.data[0])>;
@ -596,7 +523,7 @@ template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,17 +8,19 @@
#ifndef FMT_OS_H_
#define FMT_OS_H_
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif
#endif // FMT_MODULE
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
# if FMT_HAS_INCLUDE("winapifamily.h")
@ -46,6 +48,7 @@
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
@ -71,132 +74,78 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
* A reference to a null-terminated string. It can be constructed from a C
* string or `std::string`.
*
* You can use one of the following type aliases for common character types:
*
* +---------------+-----------------------------+
* | Type | Definition |
* +===============+=============================+
* | cstring_view | basic_cstring_view<char> |
* +---------------+-----------------------------+
* | wcstring_view | basic_cstring_view<wchar_t> |
* +---------------+-----------------------------+
*
* This class is most useful as a parameter type for functions that wrap C APIs.
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
/// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
/// Constructs a string reference from an `std::string` object.
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
/// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE
}
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args);
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
* Constructs a `std::system_error` object with the description of the form
*
* <message>: <system-message>
*
* where `<message>` is the formatted message and `<system-message>` is the
* system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
* If `error_code` is not a valid error code such as -1, the system message
* will look like "error -1".
*
* **Example**:
*
* // This throws a system_error with the description
* // cannot open file 'madeup': The system cannot find the file
* specified.
* // or similar (system message may vary).
* const char *filename = "madeup";
* LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) {
* throw fmt::windows_error(GetLastError(),
* "cannot open file '{}'", filename);
* }
*/
template <typename... Args>
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
@ -207,7 +156,7 @@ std::system_error windows_error(int error_code, string_view message,
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
#else
inline const std::error_category& system_category() noexcept {
inline auto system_category() noexcept -> const std::error_category& {
return std::system_category();
}
#endif // _WIN32
@ -244,7 +193,7 @@ class buffered_file {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
@ -258,24 +207,20 @@ class buffered_file {
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const noexcept { return file_; }
auto get() const noexcept -> FILE* { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
// DEPRECATED! Rename to descriptor to avoid issues with macros.
FMT_API int(fileno)() const;
FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
template <typename... T>
inline void print(string_view fmt, const T&... args) {
const auto& vargs = fmt::make_format_args(args...);
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
: fmt::vprint(file_, fmt, vargs);
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as
@ -289,6 +234,8 @@ class FMT_API file {
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public:
// Possible values for the oflag argument to the constructor.
enum {
@ -313,7 +260,7 @@ class FMT_API file {
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -324,24 +271,24 @@ class FMT_API file {
~file() noexcept;
// Returns the file descriptor.
int descriptor() const noexcept { return fd_; }
auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
long long size() const;
auto size() const -> long long;
// Attempts to read count bytes from the file into the specified buffer.
size_t read(void* buffer, size_t count);
auto read(void* buffer, size_t count) -> size_t;
// Attempts to write count bytes from the specified buffer to the file.
size_t write(const void* buffer, size_t count);
auto write(const void* buffer, size_t count) -> size_t;
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static file dup(int fd);
static auto dup(int fd) -> file;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
@ -351,24 +298,35 @@ class FMT_API file {
// necessary.
void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
buffered_file fdopen(const char* mode);
auto fdopen(const char* mode) -> buffered_file;
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
};
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size.
long getpagesize();
auto getpagesize() -> long;
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
struct buffer_size {
buffer_size() = default;
size_t value = 0;
buffer_size operator=(size_t val) const {
auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
@ -400,82 +358,82 @@ struct ostream_params {
# endif
};
FMT_END_DETAIL_NAMESPACE
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> {
class file_buffer final : public buffer<char> {
private:
file file_;
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
FMT_API static void grow(buffer<char>& buf, size_t);
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other) noexcept;
FMT_API ~file_buffer();
void flush() {
if (size() == 0) return;
file_.write(data(), size());
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
};
/**
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file.
*/
} // namespace detail
constexpr auto buffer_size = detail::buffer_size();
/// A fast output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream {
private:
FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
ostream(cstring_view path, const detail::ostream_params& params)
: buffer_(path, params) {}
public:
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
~ostream();
void flush() { buffer_.flush(); }
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); }
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt,
fmt::make_format_args(args...));
vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...));
}
};
/**
\rst
Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
* Opens a file for writing. Supported parameters passed in `params`:
*
* - `<integer>`: Flags passed to [open](
* https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
* (`file::WRONLY | file::CREATE | file::TRUNC` by default)
* - `buffer_size=<integer>`: Output buffer size
*
* **Example**:
*
* auto out = fmt::output_file("guide.txt");
* out.print("Don't {}", "Panic");
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
inline auto output_file(cstring_view path, T... params) -> ostream {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View file

@ -8,84 +8,71 @@
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <fstream>
#include <ostream>
#ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#include "format.h"
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "chrono.h" // formatbuf
FMT_BEGIN_NAMESPACE
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
// Checks if T has a user-defined operator<<.
template <typename T, typename Char, typename Enable = void>
class is_streamable {
private:
template <typename U>
static auto test(int)
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
<< std::declval<U>()) != 0>;
template <typename> static auto test(...) -> std::false_type;
using result = decltype(test<T>(0));
public:
is_streamable() = default;
static const bool value = result::value;
};
// Formatting of built-in types and arrays is intentionally disabled because
// it's handled by standard (non-ostream) formatters.
template <typename T, typename Char>
struct is_streamable<
T, Char,
enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {};
template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) {
return nullptr;
}
struct dummy_filebuf {
FILE* _Myfile;
};
template <typename T, typename U = int> struct ms_filebuf {
using type = dummy_filebuf;
};
template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> {
using type = T;
};
using filebuf_type = ms_filebuf<std::filebuf>::type;
FILE* get_file(filebuf_type& buf);
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
struct filebuf_access_tag {};
struct file_access_tag {};
} // namespace
template <typename Tag, typename FileMemberPtr, FileMemberPtr file>
class filebuf_access {
friend FILE* get_file(filebuf_type& buf) { return buf.*file; }
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
template class filebuf_access<filebuf_access_tag,
decltype(&filebuf_type::_Myfile),
&filebuf_type::_Myfile>;
inline bool write(std::filebuf& buf, fmt::string_view data) {
print(get_file(buf), data);
return true;
#if FMT_MSC_VERSION
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else
return false;
#else
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
@ -93,10 +80,6 @@ inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
if (const_check(FMT_MSC_VER)) {
auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf());
if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return;
}
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
@ -110,26 +93,31 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
template <typename T, typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt {
void set_debug_format() = delete;
template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale());
detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
@ -137,55 +125,85 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
using ostream_formatter = basic_ostream_formatter<char>;
namespace detail {
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> {
using basic_ostream_formatter<Char>::format;
// DEPRECATED!
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) const
-> OutputIt {
auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
template <typename Context>
auto format(detail::streamed_view<T> view, Context& ctx) const
-> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx);
}
};
} // namespace detail
FMT_MODULE_EXPORT
template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto buffer = basic_memory_buffer<Char>();
/**
* Returns a view that formats `value` via an ostream `operator<<`.
*
* **Example**:
*
* fmt::print("Current thread id: {}\n",
* fmt::streamed(std::this_thread::get_id()));
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
namespace detail {
inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
} // namespace detail
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_MODULE_EXPORT
template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args(args...));
FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
typename detail::vformat_args<Char>::type args) {
auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args);
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
detail::write_buffer(os, buffer);
}
FMT_MODULE_EXPORT
/**
* Prints formatted data to the stream `os`.
*
* **Example**:
*
* fmt::print(cerr, "Don't {}!", "panic");
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
if (detail::use_utf8())
vprint(os, fmt, vargs);
else
detail::vprint_directly(os, fmt, vargs);
}
FMT_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
}
FMT_END_NAMESPACE

View file

@ -8,108 +8,103 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include <ostream>
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename OutputIt, typename Char> class basic_printf_context {
template <typename Char> class basic_printf_context {
private:
OutputIt out_;
basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public:
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
using parse_context_type = basic_format_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out,
/// Constructs a `printf_context` object. References to the arguments are
/// stored in the context object so make sure they have appropriate lifetimes.
basic_printf_context(basic_appender<Char> out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
detail::locale_ref locale() { return {}; }
auto locale() -> detail::locale_ref { return {}; }
format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
};
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = max_value<int>();
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
static bool fits_in_int(bool) { return true; }
static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
static auto fits_in_int(int) -> bool { return true; }
};
class printf_precision_handler {
public:
struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
auto operator()(T) -> int {
report_error("precision is not integer");
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
struct is_zero_int {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
auto operator()(T value) -> bool {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
auto operator()(T) -> bool {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter {
private:
@ -133,22 +128,23 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
auto n = static_cast<int>(static_cast<target_type>(value));
arg_ = detail::make_arg<Context>(n);
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
arg_ = detail::make_arg<Context>(n);
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else {
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
arg_ = detail::make_arg<Context>(n);
}
}
}
@ -163,7 +159,7 @@ template <typename T, typename Context> class arg_converter {
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
arg.visit(arg_converter<T, Context>(arg, type));
}
// Converts an integer argument to char for printf.
@ -176,8 +172,8 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
auto c = static_cast<typename Context::char_type>(value);
arg_ = detail::make_arg<Context>(c);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@ -187,122 +183,126 @@ template <typename Context> class char_converter {
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
template <typename T> auto operator()(T) -> const Char* { return nullptr; }
auto operator()(const Char* s) -> const Char* { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
class printf_width_handler {
private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
auto operator()(T) -> unsigned {
report_error("width is not integer");
return 0;
}
};
// The ``printf`` argument formatter.
template <typename OutputIt, typename Char>
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The `printf` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
using context_type = basic_printf_context<Char>;
context_type& context_;
OutputIt write_null_pointer(bool is_string = false) {
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = presentation_type::none;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
public:
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); }
void operator()(monostate value) { base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt operator()(T value) {
void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (std::is_same<T, Char>::value) {
format_specs fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
if (!std::is_same<T, Char>::value) {
base::operator()(value);
return;
}
return base::operator()(value);
format_specs s = this->specs;
if (s.type != presentation_type::none && s.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
s.sign = sign::none;
s.alt = false;
s.fill = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (s.align == align::none || s.align == align::numeric)
s.align = align::right;
write<Char>(this->out, static_cast<Char>(value), s);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) {
return base::operator()(value);
void operator()(T value) {
base::operator()(value);
}
/** Formats a null-terminated C string. */
OutputIt operator()(const char* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != presentation_type::pointer);
void operator()(const char* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
/** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != presentation_type::pointer);
void operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value);
void operator()(basic_string_view<Char> value) { base::operator()(value); }
void operator()(const void* value) {
if (value)
base::operator()(value);
else
write_null_pointer();
}
/** Formats a pointer. */
OutputIt operator()(const void* value) {
return value ? base::operator()(value) : write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx =
basic_printf_parse_context<Char>(basic_string_view<Char>());
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = basic_format_parse_context<Char>({});
handle.format(parse_ctx, context_);
return this->out;
}
};
template <typename Char>
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
const Char* end) {
void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
@ -312,12 +312,10 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
specs.fill = '0';
break;
case ' ':
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
if (specs.sign != sign::plus) specs.sign = sign::space;
break;
case '#':
specs.alt = true;
@ -329,8 +327,8 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
}
template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end,
basic_format_specs<Char>& specs, GetArg get_arg) {
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
Char c = *it;
if (c >= '0' && c <= '9') {
@ -341,11 +339,11 @@ int parse_header(const Char*& it, const Char* end,
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.fill[0] = '0';
if (c == '0') specs.fill = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
if (value == -1) FMT_THROW(format_error("number is too big"));
if (value == -1) report_error("number is too big");
specs.width = value;
return arg_index;
}
@ -356,23 +354,68 @@ int parse_header(const Char*& it, const Char* end,
if (it != end) {
if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<Char>(specs), get_arg(-1)));
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
return arg_index;
}
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
case 'X':
upper = true;
FMT_FALLTHROUGH;
case 'x':
return in(t, integral_set) ? pt::hex : pt::none;
case 'E':
upper = true;
FMT_FALLTHROUGH;
case 'e':
return in(t, float_set) ? pt::exp : pt::none;
case 'F':
upper = true;
FMT_FALLTHROUGH;
case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
case 'G':
upper = true;
FMT_FALLTHROUGH;
case 'g':
return in(t, float_set) ? pt::general : pt::none;
case 'A':
upper = true;
FMT_FALLTHROUGH;
case 'a':
return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default:
return pt::none;
}
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using OutputIt = buffer_appender<Char>;
auto out = OutputIt(buf);
auto context = basic_printf_context<OutputIt, Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format);
using iterator = basic_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_format_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
@ -388,26 +431,24 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
const Char* end = parse_ctx.end();
auto it = start;
while (it != end) {
if (!detail::find<false, Char>(it, end, '%', it)) {
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
if (!find<false, Char>(it, end, '%', it)) {
it = end; // find leaves it == nullptr if it doesn't find '%'.
break;
}
Char c = *it++;
if (it != end && *it == c) {
out = detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
start = ++it;
continue;
}
out = detail::write(out, basic_string_view<Char>(
start, detail::to_unsigned(it - 1 - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
basic_format_specs<Char> specs;
auto specs = format_specs();
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) parse_ctx.on_error("argument not found");
if (arg_index == 0) report_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
@ -417,8 +458,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else {
specs.precision = 0;
}
@ -427,32 +468,30 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
if (specs.precision >= 0 && arg.is_integral()) {
// Ignore '0' for non-numeric types or if '-' present.
specs.fill = ' ';
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv);
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
if (specs.fill.template get<Char>() == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
@ -491,7 +530,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
@ -501,157 +540,117 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
type = 'd';
break;
case 'c':
visit_format_arg(
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
arg.visit(char_converter<basic_printf_context<Char>>(arg));
break;
}
}
specs.type = parse_presentation_type(type);
bool upper = false;
specs.type = parse_printf_presentation_type(type, arg.type(), upper);
if (specs.type == presentation_type::none)
parse_ctx.on_error("invalid type specifier");
report_error("invalid format specifier");
specs.upper = upper;
start = it;
// Format argument.
out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
arg.visit(printf_arg_formatter<Char>(out, specs, context));
}
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
FMT_END_DETAIL_NAMESPACE
} // namespace detail
template <typename Char>
using basic_printf_context_t =
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_context = basic_printf_context<char>;
using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
/// Constructs an `format_arg_store` object that contains references to
/// arguments and can be implicitly converted to `printf_args`.
template <typename Char = char, typename... T>
inline auto make_printf_args(T&... args)
-> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
return fmt::make_format_args<basic_printf_context<Char>>(args...);
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename Char> struct vprintf_args {
using type = basic_format_args<basic_printf_context<Char>>;
};
template <typename S, typename Char = char_t<S>>
inline auto vsprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
template <typename Char>
inline auto vsprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
return to_string(buffer);
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
return to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
*
* **Example**:
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vfprintf(
std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
* Formats `args` according to specifications in `fmt` and writes the output
* to `f`.
*
* **Example**:
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(fmt),
fmt::make_format_args<context>(args...));
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, to_string_view(fmt), args);
return vfprintf(stdout, fmt, args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
* Formats `args` according to specifications in `fmt` and writes the output
* to `stdout`.
*
* **Example**:
*
* fmt::printf("Elapsed time: %.2f seconds", 1.23);
*/
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
inline auto printf(const S& fmt, const T&... args) -> int {
return vprintf(
to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
template <typename S, typename Char = char_t<S>>
FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size());
}
template <typename S, typename... T, typename Char = char_t<S>>
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
const T&... args) -> int {
return vfprintf(os, to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
}
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

File diff suppressed because it is too large Load diff

699
external/fmt/include/fmt/std.h vendored Normal file
View file

@ -0,0 +1,699 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include "format.h"
#include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
auto path_string =
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<is_formattable<T, Char>::value &&
is_formattable<E, Char>::value>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v);
},
value);
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes<Char>(out, ec.category().name(), format_specs());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.sign = sign::plus;
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.width_ref.kind != detail::arg_id_kind::none ||
specs.precision_ref.kind != detail::arg_id_kind::none) {
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.fill = specs.fill;
outer_specs.align = specs.align;
specs.width = 0;
specs.fill = {};
specs.align = align::none;
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View file

@ -8,52 +8,92 @@
#ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_
#include <cwchar>
#include <tuple>
#include "color.h"
#include "format.h"
#include "ranges.h"
#ifndef FMT_MODULE
# include <cwchar>
# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
# endif
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN
template <typename S, typename = void> struct format_string_char {};
template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<S, enable_if_t<is_compile_string<S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename... Args>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
const Args&... args) {
return {args...};
#ifdef __cpp_char8_t
template <>
struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled()> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
return fmt::make_format_args<wformat_context>(args...);
}
inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
constexpr auto operator""_a(const wchar_t* s, size_t)
-> detail::udl_arg<wchar_t> {
return {s};
}
#endif
@ -78,136 +118,150 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep);
}
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, format_str, args);
return to_string(buf);
}
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
return vformat(to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename Char = char_t<S>,
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(
const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
inline auto vformat(const Locale& loc, const S& format_str,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), args);
return detail::vformat(loc, detail::to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
inline auto format(const Locale& loc, const S& format_str, T&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
return detail::vformat(
loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename S, typename Char = char_t<S>,
template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
typename detail::vformat_args<Char>::type args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, to_string_view(format_str), args);
return detail::get_iterator(buf);
detail::vformat_to(buf, detail::to_string_view(format_str), args);
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
return vformat_to(out, to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
detail::vformat_to(buf, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...), {});
return detail::buffer_appender<Char>(buf);
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(
OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
inline auto vformat_to(OutputIt out, const Locale& loc, const S& format_str,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
vformat_to(buf, detail::to_string_view(format_str), args,
detail::locale_ref(loc));
return detail::get_iterator(buf, out);
}
template <
typename OutputIt, typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
template <typename OutputIt, typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) ->
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
inline auto vformat_to_n(OutputIt out, size_t n,
basic_string_view<Char> format_str,
typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename S, typename... Args, typename Char = char_t<S>,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf;
detail::vformat_to(buf, to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer;
detail::vformat_to(buffer, fmt, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
auto buf = wmemory_buffer();
detail::vformat_to(buf, fmt, args);
buf.push_back(L'\0');
if (std::fputws(buf.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
@ -224,13 +278,45 @@ template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_

View file

@ -10,96 +10,34 @@
FMT_BEGIN_NAMESPACE
namespace detail {
// DEPRECATED!
template <typename T = void> struct basic_data {
FMT_API static constexpr const char digits[100][2] = {
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
0};
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
0};
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
0x1000000u | ' '};
};
#ifdef FMT_SHARED
// Required for -flto, -fivisibility=hidden and -shared to work
extern template struct basic_data<void>;
#endif
#if __cplusplus < 201703L
// DEPRECATED! These are here only for ABI compatiblity.
template <typename T> constexpr const char basic_data<T>::digits[][2];
template <typename T> constexpr const char basic_data<T>::hex_digits[];
template <typename T> constexpr const char basic_data<T>::signs[];
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
template <typename T>
constexpr const char basic_data<T>::right_padding_shifts[];
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
#endif
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(
float x) noexcept;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(
double x) noexcept;
} // namespace detail
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
template FMT_API auto dragonbox::to_decimal(float x) noexcept
-> dragonbox::decimal_fp<float>;
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
// Explicit instantiations for char.
template FMT_API auto detail::thousands_sep_impl(locale_ref)
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>;
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API auto decimal_point_impl(locale_ref) -> char;
template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, locale_ref);
// Explicit instantiations for wchar_t.
template FMT_API auto detail::thousands_sep_impl(locale_ref)
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template struct detail::basic_data<void>;
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail
FMT_END_NAMESPACE

View file

@ -12,6 +12,7 @@
#include "movie.h"
#include "gfx.h"
#include "ppu.h"
#include "cheats.h"
namespace
{
@ -115,6 +116,42 @@ static void ImGui_DrawPressedKeys(int spacing)
}
}
static void ImGui_GetWatchesText(std::string& osd_text)
{
for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++)
{
if (!watches[i].on)
break;
int32 displayNumber = 0;
char buf[64];
for (int r = 0; r < watches[i].size; r++)
displayNumber += (Cheat.CWatchRAM[(watches[i].address - 0x7E0000) + r]) << (8 * r);
if (watches[i].format == 1)
sprintf(buf, "%s,%du = %u", watches[i].desc, watches[i].size, (unsigned int)displayNumber);
else
if (watches[i].format == 3)
sprintf(buf, "%s,%dx = %X", watches[i].desc, watches[i].size, (unsigned int)displayNumber);
else // signed
{
if (watches[i].size == 1)
displayNumber = (int32)((int8)displayNumber);
else if (watches[i].size == 2)
displayNumber = (int32)((int16)displayNumber);
else if (watches[i].size == 3)
if (displayNumber >= 8388608)
displayNumber -= 16777216;
sprintf(buf, "%s,%ds = %d", watches[i].desc, watches[i].size, (int)displayNumber);
}
osd_text += buf;
osd_text += '\n';
}
}
static void ImGui_DrawTextOverlay(const char *text,
int x, int y,
int padding,
@ -246,9 +283,29 @@ bool S9xImGuiDraw(int width, int height)
}
}
std::string utf8_message;
if (Settings.DisplayWatchedAddresses)
{
ImGui_GetWatchesText(utf8_message);
}
if (!GFX.InfoString.empty())
{
auto utf8_message = sjis_to_utf8(GFX.InfoString);
utf8_message += sjis_to_utf8(GFX.InfoString);
}
if (Settings.DisplayMovieFrame && S9xMovieActive())
{
// move movie frame count into its own line if info message is active and not already a newline at end
if (!utf8_message.empty() && utf8_message.back() != '\n')
{
utf8_message += '\n';
}
utf8_message += GFX.FrameDisplayString;
}
if (!utf8_message.empty())
{
ImGui_DrawTextOverlay(utf8_message.c_str(),
settings.spacing,
height - settings.spacing,

View file

@ -1,4 +1,4 @@
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
Do this:
@ -48,6 +48,9 @@ LICENSE
RECENT REVISION HISTORY:
2.30 (2024-05-31) avoid erroneous gcc warning
2.29 (2023-05-xx) optimizations
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
2.26 (2020-07-13) many minor fixes
2.25 (2020-02-02) fix warnings
@ -108,7 +111,7 @@ RECENT REVISION HISTORY:
Cass Everitt Ryamond Barbiero github:grim210
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
Josh Tobin Matthew Gregan github:poppolopoppo
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
Brad Weinberger Matvey Cherevko github:mosra
@ -140,7 +143,7 @@ RECENT REVISION HISTORY:
// // ... x = width, y = height, n = # 8-bit components per pixel ...
// // ... replace '0' with '1'..'4' to force that many components per pixel
// // ... but 'n' will always be the number that it would have been if you said 0
// stbi_image_free(data)
// stbi_image_free(data);
//
// Standard parameters:
// int *x -- outputs image width in pixels
@ -635,7 +638,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
#endif
#endif
#ifdef _MSC_VER
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
typedef unsigned short stbi__uint16;
typedef signed short stbi__int16;
typedef unsigned int stbi__uint32;
@ -1063,6 +1066,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
}
#endif
// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
static int stbi__addints_valid(int a, int b)
{
if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
return a <= INT_MAX - b;
}
// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
static int stbi__mul2shorts_valid(int a, int b)
{
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
return a >= SHRT_MIN / b;
}
// stbi__err - error
// stbi__errpf - error returning pointer to float
// stbi__errpuc - error returning pointer to unsigned char
@ -1985,9 +2005,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
int i,j,k=0;
unsigned int code;
// build size list for each symbol (from JPEG spec)
for (i=0; i < 16; ++i)
for (j=0; j < count[i]; ++j)
for (i=0; i < 16; ++i) {
for (j=0; j < count[i]; ++j) {
h->size[k++] = (stbi_uc) (i+1);
if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
}
}
h->size[k] = 0;
// compute actual symbols (from jpeg spec)
@ -2112,6 +2135,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
// convert the huffman code to the symbol id
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
if(c < 0 || c >= 256) // symbol id out of bounds!
return -1;
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
// convert the id to a symbol
@ -2130,6 +2155,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
unsigned int k;
int sgn;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
k = stbi_lrot(j->code_buffer, n);
@ -2144,6 +2170,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
{
unsigned int k;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
k = stbi_lrot(j->code_buffer, n);
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
@ -2155,6 +2182,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
{
unsigned int k;
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
k = j->code_buffer;
j->code_buffer <<= 1;
--j->code_bits;
@ -2192,8 +2220,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
memset(data,0,64*sizeof(data[0]));
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * dequant[0]);
// decode AC components, see JPEG spec
@ -2207,6 +2237,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
// decode into unzigzag'd location
@ -2246,8 +2277,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * (1 << j->succ_low));
} else {
// refinement scan for DC coefficient
@ -2282,6 +2315,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
zig = stbi__jpeg_dezigzag[k++];
@ -3102,6 +3136,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
sizes[i] = stbi__get8(z->s);
n += sizes[i];
}
if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
L -= 17;
if (tc == 0) {
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
@ -3351,6 +3386,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
return 1;
}
static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
{
// some JPEGs have junk at end, skip over it but if we find what looks
// like a valid marker, resume there
while (!stbi__at_eof(j->s)) {
stbi_uc x = stbi__get8(j->s);
while (x == 0xff) { // might be a marker
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
x = stbi__get8(j->s);
if (x != 0x00 && x != 0xff) {
// not a stuffed zero or lead-in to another marker, looks
// like an actual marker, return it
return x;
}
// stuffed zero has x=0 now which ends the loop, meaning we go
// back to regular scan loop.
// repeated 0xff keeps trying to read the next byte of the marker.
}
}
return STBI__MARKER_none;
}
// decode image to YCbCr format
static int stbi__decode_jpeg_image(stbi__jpeg *j)
{
@ -3367,25 +3424,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j)
if (!stbi__process_scan_header(j)) return 0;
if (!stbi__parse_entropy_coded_data(j)) return 0;
if (j->marker == STBI__MARKER_none ) {
// handle 0s at the end of image data from IP Kamera 9060
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
if (x == 255) {
j->marker = stbi__get8(j->s);
break;
}
}
j->marker = stbi__skip_jpeg_junk_at_end(j);
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
}
m = stbi__get_marker(j);
if (STBI__RESTART(m))
m = stbi__get_marker(j);
} else if (stbi__DNL(m)) {
int Ld = stbi__get16be(j->s);
stbi__uint32 NL = stbi__get16be(j->s);
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
m = stbi__get_marker(j);
} else {
if (!stbi__process_marker(j, m)) return 0;
if (!stbi__process_marker(j, m)) return 1;
m = stbi__get_marker(j);
}
m = stbi__get_marker(j);
}
if (j->progressive)
stbi__jpeg_finish(j);
@ -3976,6 +4030,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
unsigned char* result;
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__errpuc("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
STBI_NOTUSED(ri);
j->s = s;
stbi__setup_jpeg(j);
@ -3989,6 +4044,7 @@ static int stbi__jpeg_test(stbi__context *s)
int r;
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
stbi__setup_jpeg(j);
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
@ -4014,6 +4070,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
int result;
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
result = stbi__jpeg_info_raw(j, x, y, comp);
STBI_FREE(j);
@ -4121,6 +4178,7 @@ typedef struct
{
stbi_uc *zbuffer, *zbuffer_end;
int num_bits;
int hit_zeof_once;
stbi__uint32 code_buffer;
char *zout;
@ -4187,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
int b,s;
if (a->num_bits < 16) {
if (stbi__zeof(a)) {
return -1; /* report error for unexpected end of data. */
if (!a->hit_zeof_once) {
// This is the first time we hit eof, insert 16 extra padding btis
// to allow us to keep going; if we actually consume any of them
// though, that is invalid data. This is caught later.
a->hit_zeof_once = 1;
a->num_bits += 16; // add 16 implicit zero bits
} else {
// We already inserted our extra 16 padding bits and are again
// out, this stream is actually prematurely terminated.
return -1;
}
} else {
stbi__fill_bits(a);
}
stbi__fill_bits(a);
}
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
if (b) {
@ -4254,17 +4323,25 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
int len,dist;
if (z == 256) {
a->zout = zout;
if (a->hit_zeof_once && a->num_bits < 16) {
// The first time we hit zeof, we inserted 16 extra zero bits into our bit
// buffer so the decoder can just do its speculative decoding. But if we
// actually consumed any of those bits (which is the case when num_bits < 16),
// the stream actually read past the end so it is malformed.
return stbi__err("unexpected end","Corrupt PNG");
}
return 1;
}
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
z -= 257;
len = stbi__zlength_base[z];
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
z = stbi__zhuffman_decode(a, &a->z_distance);
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
dist = stbi__zdist_base[z];
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
if (zout + len > a->zout_end) {
if (len > a->zout_end - zout) {
if (!stbi__zexpand(a, zout, len)) return 0;
zout = a->zout;
}
@ -4408,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
if (!stbi__parse_zlib_header(a)) return 0;
a->num_bits = 0;
a->code_buffer = 0;
a->hit_zeof_once = 0;
do {
final = stbi__zreceive(a,1);
type = stbi__zreceive(a,2);
@ -4563,9 +4641,8 @@ enum {
STBI__F_up=2,
STBI__F_avg=3,
STBI__F_paeth=4,
// synthetic filters used for first scanline to avoid needing a dummy row of 0s
STBI__F_avg_first,
STBI__F_paeth_first
// synthetic filter used for first scanline to avoid needing a dummy row of 0s
STBI__F_avg_first
};
static stbi_uc first_row_filter[5] =
@ -4574,29 +4651,56 @@ static stbi_uc first_row_filter[5] =
STBI__F_sub,
STBI__F_none,
STBI__F_avg_first,
STBI__F_paeth_first
STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
};
static int stbi__paeth(int a, int b, int c)
{
int p = a + b - c;
int pa = abs(p-a);
int pb = abs(p-b);
int pc = abs(p-c);
if (pa <= pb && pa <= pc) return a;
if (pb <= pc) return b;
return c;
// This formulation looks very different from the reference in the PNG spec, but is
// actually equivalent and has favorable data dependencies and admits straightforward
// generation of branch-free code, which helps performance significantly.
int thresh = c*3 - (a + b);
int lo = a < b ? a : b;
int hi = a < b ? b : a;
int t0 = (hi <= thresh) ? lo : c;
int t1 = (thresh <= lo) ? hi : t0;
return t1;
}
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
// adds an extra all-255 alpha channel
// dest == src is legal
// img_n must be 1 or 3
static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
{
int i;
// must process data backwards since we allow dest==src
if (img_n == 1) {
for (i=x-1; i >= 0; --i) {
dest[i*2+1] = 255;
dest[i*2+0] = src[i];
}
} else {
STBI_ASSERT(img_n == 3);
for (i=x-1; i >= 0; --i) {
dest[i*4+3] = 255;
dest[i*4+2] = src[i*3+2];
dest[i*4+1] = src[i*3+1];
dest[i*4+0] = src[i*3+0];
}
}
}
// create the png data from post-deflated data
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
{
int bytes = (depth == 16? 2 : 1);
int bytes = (depth == 16 ? 2 : 1);
stbi__context *s = a->s;
stbi__uint32 i,j,stride = x*out_n*bytes;
stbi__uint32 img_len, img_width_bytes;
stbi_uc *filter_buf;
int all_ok = 1;
int k;
int img_n = s->img_n; // copy it into a local for later
@ -4608,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
if (!a->out) return stbi__err("outofmem", "Out of memory");
// note: error exits here don't need to clean up a->out individually,
// stbi__do_png always does on error.
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
img_len = (img_width_bytes + 1) * y;
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
@ -4617,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
// so just check for raw_len < img_len always.
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
// Allocate two scan lines worth of filter workspace buffer.
filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
if (!filter_buf) return stbi__err("outofmem", "Out of memory");
// Filtering for low-bit-depth images
if (depth < 8) {
filter_bytes = 1;
width = img_width_bytes;
}
for (j=0; j < y; ++j) {
stbi_uc *cur = a->out + stride*j;
stbi_uc *prior;
// cur/prior filter buffers alternate
stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
stbi_uc *dest = a->out + stride*j;
int nk = width * filter_bytes;
int filter = *raw++;
if (filter > 4)
return stbi__err("invalid filter","Corrupt PNG");
if (depth < 8) {
if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
filter_bytes = 1;
width = img_width_bytes;
// check filter type
if (filter > 4) {
all_ok = stbi__err("invalid filter","Corrupt PNG");
break;
}
prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
// if first row, use special filter that doesn't sample previous row
if (j == 0) filter = first_row_filter[filter];
// handle first byte explicitly
for (k=0; k < filter_bytes; ++k) {
switch (filter) {
case STBI__F_none : cur[k] = raw[k]; break;
case STBI__F_sub : cur[k] = raw[k]; break;
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
case STBI__F_avg_first : cur[k] = raw[k]; break;
case STBI__F_paeth_first: cur[k] = raw[k]; break;
}
// perform actual filtering
switch (filter) {
case STBI__F_none:
memcpy(cur, raw, nk);
break;
case STBI__F_sub:
memcpy(cur, raw, filter_bytes);
for (k = filter_bytes; k < nk; ++k)
cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
break;
case STBI__F_up:
for (k = 0; k < nk; ++k)
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
break;
case STBI__F_avg:
for (k = 0; k < filter_bytes; ++k)
cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
for (k = filter_bytes; k < nk; ++k)
cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
break;
case STBI__F_paeth:
for (k = 0; k < filter_bytes; ++k)
cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
for (k = filter_bytes; k < nk; ++k)
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
break;
case STBI__F_avg_first:
memcpy(cur, raw, filter_bytes);
for (k = filter_bytes; k < nk; ++k)
cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
break;
}
if (depth == 8) {
if (img_n != out_n)
cur[img_n] = 255; // first pixel
raw += img_n;
cur += out_n;
prior += out_n;
} else if (depth == 16) {
if (img_n != out_n) {
cur[filter_bytes] = 255; // first pixel top byte
cur[filter_bytes+1] = 255; // first pixel bottom byte
}
raw += filter_bytes;
cur += output_bytes;
prior += output_bytes;
} else {
raw += 1;
cur += 1;
prior += 1;
}
raw += nk;
// this is a little gross, so that we don't switch per-pixel or per-component
if (depth < 8 || img_n == out_n) {
int nk = (width - 1)*filter_bytes;
#define STBI__CASE(f) \
case f: \
for (k=0; k < nk; ++k)
switch (filter) {
// "none" filter turns into a memcpy here; make that explicit.
case STBI__F_none: memcpy(cur, raw, nk); break;
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
}
#undef STBI__CASE
raw += nk;
} else {
STBI_ASSERT(img_n+1 == out_n);
#define STBI__CASE(f) \
case f: \
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
for (k=0; k < filter_bytes; ++k)
switch (filter) {
STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
}
#undef STBI__CASE
// the loop above sets the high byte of the pixels' alpha, but for
// 16 bit png files we also need the low byte set. we'll do that here.
if (depth == 16) {
cur = a->out + stride*j; // start at the beginning of the row again
for (i=0; i < x; ++i,cur+=output_bytes) {
cur[filter_bytes+1] = 255;
}
}
}
}
// we make a separate pass to expand bits to pixels; for performance,
// this could run two scanlines behind the above code, so it won't
// intefere with filtering but will still be in the cache.
if (depth < 8) {
for (j=0; j < y; ++j) {
stbi_uc *cur = a->out + stride*j;
stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
// unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
// png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
// expand decoded bits in cur to dest, also adding an extra alpha channel if desired
if (depth < 8) {
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
stbi_uc *in = cur;
stbi_uc *out = dest;
stbi_uc inb = 0;
stbi__uint32 nsmp = x*img_n;
// note that the final byte might overshoot and write more data than desired.
// we can allocate enough data that this never writes out of memory, but it
// could also overwrite the next scanline. can it overwrite non-empty data
// on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
// so we need to explicitly clamp the final ones
// expand bits to bytes first
if (depth == 4) {
for (k=x*img_n; k >= 2; k-=2, ++in) {
*cur++ = scale * ((*in >> 4) );
*cur++ = scale * ((*in ) & 0x0f);
for (i=0; i < nsmp; ++i) {
if ((i & 1) == 0) inb = *in++;
*out++ = scale * (inb >> 4);
inb <<= 4;
}
if (k > 0) *cur++ = scale * ((*in >> 4) );
} else if (depth == 2) {
for (k=x*img_n; k >= 4; k-=4, ++in) {
*cur++ = scale * ((*in >> 6) );
*cur++ = scale * ((*in >> 4) & 0x03);
*cur++ = scale * ((*in >> 2) & 0x03);
*cur++ = scale * ((*in ) & 0x03);
for (i=0; i < nsmp; ++i) {
if ((i & 3) == 0) inb = *in++;
*out++ = scale * (inb >> 6);
inb <<= 2;
}
if (k > 0) *cur++ = scale * ((*in >> 6) );
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
} else if (depth == 1) {
for (k=x*img_n; k >= 8; k-=8, ++in) {
*cur++ = scale * ((*in >> 7) );
*cur++ = scale * ((*in >> 6) & 0x01);
*cur++ = scale * ((*in >> 5) & 0x01);
*cur++ = scale * ((*in >> 4) & 0x01);
*cur++ = scale * ((*in >> 3) & 0x01);
*cur++ = scale * ((*in >> 2) & 0x01);
*cur++ = scale * ((*in >> 1) & 0x01);
*cur++ = scale * ((*in ) & 0x01);
} else {
STBI_ASSERT(depth == 1);
for (i=0; i < nsmp; ++i) {
if ((i & 7) == 0) inb = *in++;
*out++ = scale * (inb >> 7);
inb <<= 1;
}
if (k > 0) *cur++ = scale * ((*in >> 7) );
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
}
if (img_n != out_n) {
int q;
// insert alpha = 255
cur = a->out + stride*j;
// insert alpha=255 values if desired
if (img_n != out_n)
stbi__create_png_alpha_expand8(dest, dest, x, img_n);
} else if (depth == 8) {
if (img_n == out_n)
memcpy(dest, cur, x*img_n);
else
stbi__create_png_alpha_expand8(dest, cur, x, img_n);
} else if (depth == 16) {
// convert the image data from big-endian to platform-native
stbi__uint16 *dest16 = (stbi__uint16*)dest;
stbi__uint32 nsmp = x*img_n;
if (img_n == out_n) {
for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
*dest16 = (cur[0] << 8) | cur[1];
} else {
STBI_ASSERT(img_n+1 == out_n);
if (img_n == 1) {
for (q=x-1; q >= 0; --q) {
cur[q*2+1] = 255;
cur[q*2+0] = cur[q];
for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
dest16[0] = (cur[0] << 8) | cur[1];
dest16[1] = 0xffff;
}
} else {
STBI_ASSERT(img_n == 3);
for (q=x-1; q >= 0; --q) {
cur[q*4+3] = 255;
cur[q*4+2] = cur[q*3+2];
cur[q*4+1] = cur[q*3+1];
cur[q*4+0] = cur[q*3+0];
for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
dest16[0] = (cur[0] << 8) | cur[1];
dest16[1] = (cur[2] << 8) | cur[3];
dest16[2] = (cur[4] << 8) | cur[5];
dest16[3] = 0xffff;
}
}
}
}
} else if (depth == 16) {
// force the image data from big-endian to platform-native.
// this is done in a separate pass due to the decoding relying
// on the data being untouched, but could probably be done
// per-line during decode if care is taken.
stbi_uc *cur = a->out;
stbi__uint16 *cur16 = (stbi__uint16*)cur;
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
*cur16 = (cur[0] << 8) | cur[1];
}
}
STBI_FREE(filter_buf);
if (!all_ok) return 0;
return 1;
}
@ -4955,7 +5010,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_set = 1;
@ -5064,14 +5119,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!pal_img_n) {
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
if (scan == STBI__SCAN_header) return 1;
} else {
// if paletted, then pal_n is our final components, and
// img_n is # components to decompress/filter.
s->img_n = 1;
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
// if SCAN_header, have to scan to see if we have a tRNS
}
// even with SCAN_header, have to scan to see if we have a tRNS
break;
}
@ -5103,10 +5157,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
has_trans = 1;
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
if (z->depth == 16) {
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
} else {
for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
for (k = 0; k < s->img_n && k < 3; ++k)
tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
}
}
break;
@ -5115,7 +5173,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
case STBI__PNG_TYPE('I','D','A','T'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
if (scan == STBI__SCAN_header) {
// header scan definitely stops at first IDAT
if (pal_img_n)
s->img_n = pal_img_n;
return 1;
}
if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
stbi__uint32 idata_limit_old = idata_limit;
@ -5498,8 +5562,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
psize = (info.offset - info.extra_read - info.hsz) >> 2;
}
if (psize == 0) {
if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) {
return stbi__errpuc("bad offset", "Corrupt BMP");
// accept some number of extra bytes after the header, but if the offset points either to before
// the header ends or implies a large amount of extra data, reject the file as malformed
int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
int header_limit = 1024; // max we actually read is below 256 bytes currently.
int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
return stbi__errpuc("bad header", "Corrupt BMP");
}
// we established that bytes_read_so_far is positive and sensible.
// the first half of this test rejects offsets that are either too small positives, or
// negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
// ensures the number computed in the second half of the test can't overflow.
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
return stbi__errpuc("bad offset", "Corrupt BMP");
} else {
stbi__skip(s, info.offset - bytes_read_so_far);
}
}
@ -7187,12 +7265,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
// Run
value = stbi__get8(s);
count -= 128;
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = value;
} else {
// Dump
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = stbi__get8(s);
}
@ -7446,10 +7524,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8));
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
STBI_FREE(out);
return stbi__errpuc("bad PNM", "PNM file truncated");
}
if (req_comp && req_comp != s->img_n) {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
if (ri->bits_per_channel == 16) {
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
} else {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
}
if (out == NULL) return out; // stbi__convert_format frees input on failure
}
return out;
@ -7486,6 +7571,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c)
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
value = value*10 + (*c - '0');
*c = (char) stbi__get8(s);
if((value > 214748364) || (value == 214748364 && *c > '7'))
return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
}
return value;
@ -7516,9 +7603,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
stbi__pnm_skip_whitespace(s, &c);
*x = stbi__pnm_getinteger(s, &c); // read width
if(*x == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
*y = stbi__pnm_getinteger(s, &c); // read height
if (*y == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
maxv = stbi__pnm_getinteger(s, &c); // read max value

View file

@ -1069,7 +1069,6 @@ struct ColorGradientARGB
};
}
void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
{
static_assert(SCALE_FACTOR_MAX == 6, "");

View file

@ -68,23 +68,23 @@ list(APPEND ARGS ${SDL2_CFLAGS} ${GTK_CFLAGS} ${XRANDR_CFLAGS})
list(APPEND LIBS ${X11} ${XEXT} ${CMAKE_DL_LIBS} ${SDL2_LIBRARIES} ${GTK_LIBRARIES} ${XRANDR_LIBRARIES})
list(APPEND SOURCES src/gtk_display_driver_opengl.cpp
../common/video/glx_context.cpp
../shaders/glsl.cpp
../shaders/shader_helpers.cpp
../vulkan/slang_helpers.cpp
../vulkan/slang_helpers.hpp
../vulkan/slang_preset_ini.cpp
../vulkan/slang_preset_ini.hpp
../common/video/opengl/glx_context.cpp
../common/video/opengl/shaders/glsl.cpp
../common/video/opengl/shaders/shader_helpers.cpp
../common/video/vulkan/slang_helpers.cpp
../common/video/vulkan/slang_helpers.hpp
../common/video/vulkan/slang_preset_ini.cpp
../common/video/vulkan/slang_preset_ini.hpp
../external/glad/src/glx.c
../external/glad/src/egl.c
../external/glad/src/gl.c
src/gtk_shader_parameters.cpp
../vulkan/std_chrono_throttle.cpp
../vulkan/std_chrono_throttle.hpp)
../common/video/std_chrono_throttle.cpp
../common/video/std_chrono_throttle.hpp)
list(APPEND INCLUDES ../external/glad/include)
if(USE_SLANG)
list(APPEND SOURCES ../shaders/slang.cpp)
list(APPEND SOURCES ../common/video/opengl/shaders/slang.cpp)
list(APPEND INCLUDES ../external/glslang)
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS CACHE BOOL ON)
@ -116,26 +116,28 @@ if(USE_SLANG)
list(APPEND INCLUDES ../external/VulkanMemoryAllocator-Hpp/include)
list(APPEND INCLUDES ../external/stb)
list(APPEND SOURCES ../external/stb/stb_image_implementation.cpp)
list(APPEND SOURCES ../vulkan/slang_shader.cpp
../vulkan/slang_shader.hpp
../vulkan/slang_preset.cpp
../vulkan/slang_preset.hpp
../vulkan/vulkan_hpp_storage.cpp
../vulkan/vk_mem_alloc_implementation.cpp
../vulkan/vulkan_context.cpp
../vulkan/vulkan_context.hpp
../vulkan/vulkan_texture.cpp
../vulkan/vulkan_texture.hpp
../vulkan/vulkan_swapchain.cpp
../vulkan/vulkan_swapchain.hpp
../vulkan/vulkan_slang_pipeline.cpp
../vulkan/vulkan_slang_pipeline.hpp
../vulkan/vulkan_pipeline_image.cpp
../vulkan/vulkan_pipeline_image.hpp
../vulkan/vulkan_shader_chain.cpp
../vulkan/vulkan_shader_chain.hpp
../vulkan/vulkan_simple_output.hpp
../vulkan/vulkan_simple_output.cpp
list(APPEND SOURCES ../common/video/vulkan/slang_shader.cpp
../common/video/vulkan/slang_shader.hpp
../common/video/vulkan/slang_preset.cpp
../common/video/vulkan/slang_preset.hpp
../common/video/vulkan/vulkan_hpp_storage.cpp
../common/video/vulkan/vk_mem_alloc_implementation.cpp
../common/video/vulkan/vulkan_context.cpp
../common/video/vulkan/vulkan_context.hpp
../common/video/vulkan/vulkan_common.cpp
../common/video/vulkan/vulkan_common.hpp
../common/video/vulkan/vulkan_texture.cpp
../common/video/vulkan/vulkan_texture.hpp
../common/video/vulkan/vulkan_swapchain.cpp
../common/video/vulkan/vulkan_swapchain.hpp
../common/video/vulkan/vulkan_slang_pipeline.cpp
../common/video/vulkan/vulkan_slang_pipeline.hpp
../common/video/vulkan/vulkan_pipeline_image.cpp
../common/video/vulkan/vulkan_pipeline_image.hpp
../common/video/vulkan/vulkan_shader_chain.cpp
../common/video/vulkan/vulkan_shader_chain.hpp
../common/video/vulkan/vulkan_simple_output.hpp
../common/video/vulkan/vulkan_simple_output.cpp
src/gtk_display_driver_vulkan.cpp
src/gtk_display_driver_vulkan.h
../external/imgui/imgui_impl_vulkan.cpp)
@ -154,13 +156,13 @@ list(APPEND INCLUDES ../external/imgui)
if(USE_WAYLAND)
pkg_check_modules(WAYLAND REQUIRED wayland-client wayland-egl)
list(APPEND DEFINES "USE_WAYLAND")
list(APPEND SOURCES ../common/video/wayland_egl_context.cpp
../common/video/wayland_egl_context.hpp
../common/video/wayland_surface.cpp
../common/video/wayland_surface.hpp
../common/video/wayland-idle-inhibit-unstable-v1.c
../common/video/viewporter-client-protocol.c
../common/video/fractional-scale-v1.c)
list(APPEND SOURCES ../common/video/opengl/wayland_egl_context.cpp
../common/video/opengl/wayland_egl_context.hpp
../common/video/wayland/wayland_surface.cpp
../common/video/wayland/wayland_surface.hpp
../common/video/wayland/wayland-idle-inhibit-unstable-v1.c
../common/video/wayland/viewporter-client-protocol.c
../common/video/wayland/fractional-scale-v1.c)
list(APPEND ARGS ${WAYLAND_CFLAGS})
list(APPEND LIBS ${WAYLAND_LIBRARIES})
endif()
@ -388,16 +390,16 @@ target_compile_definitions(snes9x-gtk PRIVATE ${DEFINES})
if(USE_SLANG)
add_executable(slang_test EXCLUDE_FROM_ALL
../vulkan/slang_helpers.cpp
../vulkan/slang_helpers.hpp
../vulkan/slang_shader.cpp
../vulkan/slang_shader.hpp
../vulkan/slang_preset.cpp
../vulkan/slang_preset.hpp
../vulkan/slang_preset_ini.cpp
../vulkan/slang_preset_ini.hpp
../vulkan/vulkan_hpp_storage.cpp
../vulkan/slang_preset_test.cpp
../common/video/vulkan/slang_helpers.cpp
../common/video/vulkan/slang_helpers.hpp
../common/video/vulkan/slang_shader.cpp
../common/video/vulkan/slang_shader.hpp
../common/video/vulkan/slang_preset.cpp
../common/video/vulkan/slang_preset.hpp
../common/video/vulkan/slang_preset_ini.cpp
../common/video/vulkan/slang_preset_ini.hpp
../common/video/vulkan/vulkan_hpp_storage.cpp
../common/video/vulkan/slang_preset_test.cpp
../conffile.cpp
../stream.cpp)

1874
gtk/po/sv.po Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,37 +4,31 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifdef DEBUGGER
#include "debug.h"
#include "missing.h"
#endif
#include "port.h"
#include "filter_epx_unsafe.h"
#undef AVERAGE_565
#define AVERAGE_565(el0, el1) (((el0) & (el1)) + ((((el0) ^ (el1)) & 0xF7DE) >> 1))
/* Allows vertical overlap. We need this to avoid seams when threading */
void EPX_16_unsafe (uint8 *srcPtr,
void EPX_16_unsafe (uint8_t *srcPtr,
int srcPitch,
uint8 *dstPtr,
uint8_t *dstPtr,
int dstPitch,
int width,
int height)
{
uint16 colorX, colorA, colorB, colorC, colorD;
uint16 *sP, *uP, *lP;
uint32 *dP1, *dP2;
uint16_t colorX, colorA, colorB, colorC, colorD;
uint16_t *sP, *uP, *lP;
uint32_t *dP1, *dP2;
int w;
for (; height; height--)
{
sP = (uint16 *) srcPtr;
uP = (uint16 *) (srcPtr - srcPitch);
lP = (uint16 *) (srcPtr + srcPitch);
dP1 = (uint32 *) dstPtr;
dP2 = (uint32 *) (dstPtr + dstPitch);
sP = (uint16_t *) srcPtr;
uP = (uint16_t *) (srcPtr - srcPitch);
lP = (uint16_t *) (srcPtr + srcPitch);
dP1 = (uint32_t *) dstPtr;
dP2 = (uint32_t *) (dstPtr + dstPitch);
// left edge
@ -112,25 +106,25 @@ void EPX_16_unsafe (uint8 *srcPtr,
}
/* Blends with edge pixel instead of just using it directly. */
void EPX_16_smooth_unsafe (uint8 *srcPtr,
void EPX_16_smooth_unsafe (uint8_t *srcPtr,
int srcPitch,
uint8 *dstPtr,
uint8_t *dstPtr,
int dstPitch,
int width,
int height)
{
uint16 colorX, colorA, colorB, colorC, colorD;
uint16 *sP, *uP, *lP;
uint32 *dP1, *dP2;
uint16_t colorX, colorA, colorB, colorC, colorD;
uint16_t *sP, *uP, *lP;
uint32_t *dP1, *dP2;
int w;
for (; height; height--)
{
sP = (uint16 *) srcPtr;
uP = (uint16 *) (srcPtr - srcPitch);
lP = (uint16 *) (srcPtr + srcPitch);
dP1 = (uint32 *) dstPtr;
dP2 = (uint32 *) (dstPtr + dstPitch);
sP = (uint16_t *) srcPtr;
uP = (uint16_t *) (srcPtr - srcPitch);
lP = (uint16_t *) (srcPtr + srcPitch);
dP1 = (uint32_t *) dstPtr;
dP2 = (uint32_t *) (dstPtr + dstPitch);
// left edge

View file

@ -7,7 +7,8 @@
#ifndef __FILTER_EPX_UNSAFE_H
#define __FILTER_EPX_UNSAFE_H
void EPX_16_unsafe (uint8 *, int, uint8 *, int, int, int);
void EPX_16_smooth_unsafe (uint8 *, int, uint8 *, int, int, int);
#include <cstdint>
void EPX_16_unsafe(uint8_t *, int, uint8_t *, int, int, int);
void EPX_16_smooth_unsafe(uint8_t *, int, uint8_t *, int, int, int);
#endif /* __FILTER_EPX_UNSAFE_H */

View file

@ -5,7 +5,6 @@
\*****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include "gtk_builder_window.h"
extern const unsigned char snes9x_ui[];

View file

@ -7,7 +7,6 @@
#include "gtk_s9x.h"
#include "gtk_cheat.h"
#include "cheats.h"
#include "display.h"
enum {
COLUMN_ENABLED = 0,

View file

@ -20,10 +20,18 @@
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/gdkwayland.h>
inline bool is_wayland()
{
return GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
}
#endif
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
inline bool is_x11()
{
return GDK_IS_X11_DISPLAY(gdk_display_get_default());
}
#endif
#endif

View file

@ -4,8 +4,6 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <filesystem>
@ -721,9 +719,15 @@ void Snes9xConfig::rebind_keys()
cmd = S9xGetPortCommandT("{Mouse1 L,Superscope Fire,Justifier1 Trigger}");
S9xMapButton(BINDING_MOUSE_BUTTON0, cmd, false);
cmd = S9xGetPortCommandT("{Justifier1 AimOffscreen Trigger,Superscope AimOffscreen}");
cmd = S9xGetPortCommandT("Superscope ToggleTurbo");
S9xMapButton(BINDING_MOUSE_BUTTON1, cmd, false);
cmd = S9xGetPortCommandT("{Mouse1 R,Superscope Cursor,Justifier1 Start}");
cmd = S9xGetPortCommandT("{Mouse1 R,Superscope Pause,Justifier1 Start}");
S9xMapButton(BINDING_MOUSE_BUTTON2, cmd, false);
cmd = S9xGetPortCommandT("Superscope Cursor");
S9xMapButton(BINDING_MOUSE_BUTTON0 + 7, cmd, false);
cmd = S9xGetPortCommandT("{Superscope AimOffscreen,Justifier1 AimOffscreen}");
S9xMapButton(BINDING_MOUSE_BUTTON0 + 8, cmd, false);
}

View file

@ -5,7 +5,6 @@
\*****************************************************************************/
#include <fcntl.h>
#include <filesystem>
#include "SDL_joystick.h"
#include "fscompat.h"
@ -139,8 +138,28 @@ bool S9xPollAxis(uint32 id, int16 *value)
return true;
}
static bool using_superscope()
{
for (int i = 0; i < 2; i++)
{
enum controllers ctl;
int8_t id1, id2, id3, id4;
S9xGetController(i, &ctl, &id1, &id2, &id3, &id4);
if (ctl == CTL_SUPERSCOPE)
return true;
}
return false;
}
bool S9xPollPointer(uint32 id, int16 *x, int16 *y)
{
if (using_superscope())
{
top_level->snes_mouse_x = std::clamp(top_level->snes_mouse_x, 0.0, 256.0);
top_level->snes_mouse_y = std::clamp(top_level->snes_mouse_y, 0.0, 239.0);
}
*x = top_level->snes_mouse_x;
*y = top_level->snes_mouse_y;

View file

@ -13,13 +13,8 @@
#include "gtk_display_driver_gtk.h"
#include "snes9x.h"
#include "memmap.h"
#include "cpuexec.h"
#include "ppu.h"
#include "gfx.h"
#include "netplay.h"
#include "controls.h"
#include "movie.h"
#if defined(USE_XV) && defined(GDK_WINDOWING_X11)
#include "gtk_display_driver_xv.h"
@ -789,7 +784,7 @@ void S9xQueryDrivers()
gui_config->allow_xv = false;
#if defined(USE_XV) && defined(GDK_WINDOWING_X11)
if (GDK_IS_X11_DISPLAY(gdk_display))
if (is_x11())
gui_config->allow_xv = S9xXVDisplayDriver::query_availability();
#endif
@ -797,7 +792,7 @@ void S9xQueryDrivers()
gui_config->allow_xrandr = false;
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY(gdk_display))
if (is_x11())
{
Display *dpy = gdk_x11_display_get_xdisplay(gdk_display);
Window xid = gdk_x11_window_get_xid(top_level->window->get_window()->gobj());
@ -912,7 +907,7 @@ static void S9xInitDriver()
{
// Only OpenGL is supported on Wayland
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default()))
if (is_wayland())
{
if (gui_config->display_driver != "vulkan")
gui_config->display_driver = "opengl";

View file

@ -25,6 +25,8 @@ class S9xDisplayDriver
virtual bool can_throttle() { return false; };
virtual int get_width() = 0;
virtual int get_height() = 0;
virtual void shrink() {};
virtual void regrow() {};
protected:
Snes9xWindow *window;

View file

@ -7,7 +7,6 @@
#ifndef __GTK_DISPLAY_DRIVER_GTK_H
#define __GTK_DISPLAY_DRIVER_GTK_H
#include "gtk_s9x.h"
#include "gtk_display_driver.h"
class S9xGTKDisplayDriver : public S9xDisplayDriver

View file

@ -346,13 +346,13 @@ void S9xOpenGLDisplayDriver::refresh()
void S9xOpenGLDisplayDriver::resize()
{
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(gdk_window))
if (is_wayland())
{
((WaylandEGLContext *)context)->resize(get_metrics(*drawing_area));
}
#endif
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW(gdk_window))
if (is_x11())
{
context->resize();
}
@ -371,7 +371,7 @@ bool S9xOpenGLDisplayDriver::create_context()
GdkDisplay *gdk_display = drawing_area->get_display()->gobj();
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(gdk_window))
if (is_wayland())
{
wl_surface *surface = gdk_wayland_window_get_wl_surface(drawing_area->get_window()->gobj());
wl_display *display = gdk_wayland_display_get_wl_display(drawing_area->get_display()->gobj());
@ -381,7 +381,7 @@ bool S9xOpenGLDisplayDriver::create_context()
}
#endif
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW(gdk_window))
if (is_x11())
{
if (!glx.attach(gdk_x11_display_get_xdisplay(gdk_display), gdk_x11_window_get_xid(gdk_window)))
return false;
@ -494,23 +494,7 @@ void S9xOpenGLDisplayDriver::deinit()
int S9xOpenGLDisplayDriver::query_availability()
{
GdkDisplay *gdk_display = gdk_display_get_default();
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY(gdk_display))
{
return 1;
}
#endif
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY(gdk_display))
{
return 1;
}
#endif
return 0;
return 1;
}
bool S9xOpenGLDisplayDriver::is_ready()
@ -522,3 +506,23 @@ bool S9xOpenGLDisplayDriver::is_ready()
return false;
}
void S9xOpenGLDisplayDriver::shrink()
{
#ifdef GDK_WINDOWING_WAYLAND
if (is_wayland())
{
((WaylandEGLContext *)context)->shrink();
}
#endif
}
void S9xOpenGLDisplayDriver::regrow()
{
#ifdef GDK_WINDOWING_WAYLAND
if (is_wayland())
{
((WaylandEGLContext *)context)->regrow();
}
#endif
}

View file

@ -12,18 +12,18 @@
#include <glad/gl.h>
#include "common/video/opengl_context.hpp"
#include "common/video/opengl/opengl_context.hpp"
#include "gtk_compat.h"
#ifdef GDK_WINDOWING_X11
#include "common/video/glx_context.hpp"
#include "common/video/opengl/glx_context.hpp"
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include "common/video/wayland_egl_context.hpp"
#include "common/video/opengl/wayland_egl_context.hpp"
#endif
#include "shaders/glsl.h"
#include "vulkan/std_chrono_throttle.hpp"
#include "common/video/opengl/shaders/glsl.h"
#include "common/video/std_chrono_throttle.hpp"
#define BUFFER_OFFSET(i) ((char *)(i))
@ -42,6 +42,8 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver
bool can_throttle() override { return true; }
int get_width() final override { return output_window_width; }
int get_height() final override { return output_window_height; }
void shrink() override;
void regrow() override;
private:
bool opengl_defaults();

View file

@ -9,7 +9,6 @@
#include "gtk_display_driver_vulkan.h"
#include "gtk_shader_parameters.h"
#include "snes9x.h"
#include "gfx.h"
#include "fmt/format.h"
#include "snes9x_imgui.h"
@ -66,12 +65,12 @@ bool S9xVulkanDisplayDriver::init_imgui()
init_info.Device = context->device;;
init_info.QueueFamily = context->graphics_queue_family_index;
init_info.Queue = context->queue;
init_info.DescriptorPool = imgui_descriptor_pool.get();
init_info.DescriptorPool = static_cast<VkDescriptorPool>(imgui_descriptor_pool.get());
init_info.Subpass = 0;
init_info.MinImageCount = context->swapchain->get_num_frames();
init_info.ImageCount = context->swapchain->get_num_frames();
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
ImGui_ImplVulkan_Init(&init_info, context->swapchain->get_render_pass());
ImGui_ImplVulkan_Init(&init_info, static_cast<VkRenderPass>(context->swapchain->get_render_pass()));
auto cmd = context->begin_cmd_buffer();
ImGui_ImplVulkan_CreateFontsTexture(cmd);
@ -90,8 +89,11 @@ void S9xVulkanDisplayDriver::refresh()
int new_width, new_height;
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
if (is_wayland())
{
std::tie(new_width, new_height) = wayland_surface->get_size_for_metrics(get_metrics(*drawing_area));
context->swapchain->set_desired_size(new_width, new_height);
}
else
#endif
{
@ -101,13 +103,13 @@ void S9xVulkanDisplayDriver::refresh()
if (new_width != current_width || new_height != current_height)
{
context->recreate_swapchain(new_width, new_height);
context->recreate_swapchain();
context->wait_idle();
current_width = new_width;
current_height = new_height;
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
if (is_wayland())
wayland_surface->resize(get_metrics(*drawing_area));
#endif
}
@ -121,26 +123,35 @@ int S9xVulkanDisplayDriver::init()
context = std::make_unique<Vulkan::Context>();
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
if (is_wayland())
{
wayland_surface = std::make_unique<WaylandSurface>();
wl_surface *surface = gdk_wayland_window_get_wl_surface(drawing_area->get_window()->gobj());
wl_display *display = gdk_wayland_display_get_wl_display(drawing_area->get_display()->gobj());
if (!wayland_surface->attach(display, surface, get_metrics(*drawing_area)))
return -1;
if (!context->init_wayland(wayland_surface->display, wayland_surface->child, current_width, current_height))
context->swapchain->set_desired_size(current_width, current_height);
if (!wayland_surface->attach(display, surface, get_metrics(*drawing_area)) ||
!context->init_wayland() ||
!context->create_wayland_surface(wayland_surface->display, wayland_surface->child) ||
!context->create_swapchain())
{
context.reset();
return -1;
}
}
#endif
if (GDK_IS_X11_WINDOW(drawing_area->get_window()->gobj()))
if (is_x11())
{
display = gdk_x11_display_get_xdisplay(drawing_area->get_display()->gobj());
xid = gdk_x11_window_get_xid(drawing_area->get_window()->gobj());
if (!context->init_Xlib(display, xid))
if (!context->init_Xlib() ||
!context->create_Xlib_surface(display, xid) ||
!context->create_swapchain())
{
context.reset();
return -1;
}
}
device = context->device;
@ -192,6 +203,8 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int
if (!context)
return;
context->swapchain->set_vsync(gui_config->sync_to_vblank);
if (S9xImGuiDraw(current_width, current_height))
{
ImDrawData *draw_data = ImGui::GetDrawData();
@ -222,11 +235,13 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int
throttle.wait_for_frame_and_rebase_time();
}
context->swapchain->set_vsync(gui_config->sync_to_vblank);
context->swapchain->swap();
if (gui_config->reduce_input_lag)
{
context->wait_idle();
context->swapchain->present_wait();
}
}
}
@ -253,4 +268,20 @@ void S9xVulkanDisplayDriver::save(const char *filename)
bool S9xVulkanDisplayDriver::is_ready()
{
return true;
}
void S9xVulkanDisplayDriver::shrink()
{
#ifdef GDK_WINDOWING_WAYLAND
if (is_wayland())
wayland_surface->shrink();
#endif
}
void S9xVulkanDisplayDriver::regrow()
{
#ifdef GDK_WINDOWING_WAYLAND
if (is_wayland())
wayland_surface->regrow();
#endif
}

View file

@ -7,13 +7,13 @@
#pragma once
#include "gtk_s9x.h"
#include "gtk_display_driver.h"
#include "vulkan/vulkan_context.hpp"
#include "vulkan/vulkan_shader_chain.hpp"
#include "vulkan/vulkan_simple_output.hpp"
#include "vulkan/std_chrono_throttle.hpp"
#include "common/video/vulkan/vulkan_context.hpp"
#include "common/video/vulkan/vulkan_shader_chain.hpp"
#include "common/video/vulkan/vulkan_simple_output.hpp"
#include "common/video/std_chrono_throttle.hpp"
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include "common/video/wayland_surface.hpp"
#include "common/video/wayland/wayland_surface.hpp"
#endif
class S9xVulkanDisplayDriver : public S9xDisplayDriver
@ -31,6 +31,8 @@ class S9xVulkanDisplayDriver : public S9xDisplayDriver
bool can_throttle() override { return true; }
int get_width() final override { return current_width; }
int get_height() final override { return current_height; }
void shrink() override;
void regrow() override;
static int query_availability();

View file

@ -5,7 +5,6 @@
\*****************************************************************************/
#include <sys/stat.h>
#include <errno.h>
#include <string>
#include <filesystem>

View file

@ -14,7 +14,6 @@
#include "fmt/format.h"
#include "snes9x.h"
#include "gfx.h"
#include "display.h"
#define SAME_AS_GAME gettext("Same location as current game")

View file

@ -28,8 +28,6 @@
#include "ppu.h"
#include "fmt/format.h"
#include <iomanip>
static void S9xThrottle(int);
static void S9xCheckPointerTimer();
static bool S9xIdleFunc();
@ -87,7 +85,7 @@ int main(int argc, char *argv[])
top_level = new Snes9xWindow(gui_config);
#ifdef GDK_WINDOWING_X11
if (!GDK_IS_X11_WINDOW(top_level->window->get_window()->gobj()))
if (!is_x11())
XInitThreads();
#endif
@ -383,35 +381,35 @@ void S9xMessage(int type, int number, const char *message)
case S9X_TRACE:
case S9X_DEBUG:
{
g_debug(message);
g_debug("%s", message);
break;
}
case S9X_WARNING:
{
g_warning(message);
g_warning("%s", message);
break;
}
case S9X_INFO:
{
g_info(message);
g_message(message);
g_info("%s", message);
g_message("%s", message);
break;
}
case S9X_ERROR:
{
// GLibs g_critical() does not terminate the process
g_critical(message);
g_critical("%s", message);
break;
}
case S9X_FATAL_ERROR:
{
// GLibs g_error() terminates the process
g_error(message);
g_error("%s", message);
break;
}
default:
{
g_message(message);
g_message("%s", message);
}
}
}

View file

@ -35,7 +35,6 @@
#include "snes9x.h"
#include "controls.h"
#include "movie.h"
#include "display.h"
#include "apu/apu.h"
#include "memmap.h"
#include "cpuexec.h"
@ -275,43 +274,20 @@ void Snes9xWindow::connect_signals()
bool Snes9xWindow::button_press(GdkEventButton *event)
{
if (S9xIsMousePluggedIn())
{
switch (event->button)
{
case 1:
S9xReportButton(BINDING_MOUSE_BUTTON0, 1);
break;
case 2:
S9xReportButton(BINDING_MOUSE_BUTTON2, 1);
break;
case 3:
S9xReportButton(BINDING_MOUSE_BUTTON1, 1);
break;
}
}
else if (event->button == 3)
if (!S9xIsMousePluggedIn() && event->button == 3)
{
get_object<Gtk::Menu>("view_menu_menu")->popup_at_pointer(nullptr);
return false;
}
S9xReportButton(BINDING_MOUSE_BUTTON0 + event->button - 1, true);
return false;
}
bool Snes9xWindow::button_release(GdkEventButton *event)
{
switch (event->button)
{
case 1:
S9xReportButton(BINDING_MOUSE_BUTTON0, 0);
break;
case 2:
S9xReportButton(BINDING_MOUSE_BUTTON1, 0);
break;
case 3:
S9xReportButton(BINDING_MOUSE_BUTTON2, 0);
break;
}
S9xReportButton(BINDING_MOUSE_BUTTON0 + event->button - 1, false);
return false;
}
@ -358,42 +334,29 @@ void Snes9xWindow::port_activate(const char *name)
if (!item->get_active())
return;
if (!strcasecmp(name, "joypad1"))
{
S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);
}
else if (!strcasecmp(name, "joypad2"))
{
S9xSetController(1, CTL_JOYPAD, 1, 0, 0, 0);
}
else if (!strcasecmp(name, "mouse1"))
{
S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0);
}
else if (!strcasecmp(name, "mouse2"))
{
S9xSetController(1, CTL_MOUSE, 0, 0, 0, 0);
}
else if (!strcasecmp(name, "superscope1"))
{
S9xSetController(0, CTL_SUPERSCOPE, 0, 0, 0, 0);
}
else if (!strcasecmp(name, "superscope2"))
{
S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0);
}
else if (!strcasecmp(name, "multitap1"))
{
S9xSetController(0, CTL_MP5, 0, 1, 2, 3);
}
else if (!strcasecmp(name, "multitap2"))
{
S9xSetController(1, CTL_MP5, 1, 2, 3, 4);
}
else if (!strcasecmp(name, "nothingpluggedin2"))
{
S9xSetController(1, CTL_NONE, 0, 0, 0, 0);
}
struct {
const char *name;
int port;
enum controllers controller;
uint8_t id1, id2, id3, id4;
} map[] = {
{ "joypad1", 0, CTL_JOYPAD, 0, 0, 0, 0 },
{ "joypad2", 1, CTL_JOYPAD, 1, 0, 0, 0 },
{ "mouse1", 0, CTL_MOUSE, 0, 0, 0, 0 },
{ "mouse2", 1, CTL_MOUSE, 0, 0, 0, 0 },
{ "superscope1", 0, CTL_SUPERSCOPE, 0, 0, 0, 0 },
{ "superscope2", 1, CTL_SUPERSCOPE, 0, 0, 0, 0 },
{ "multitap1", 0, CTL_MP5, 0, 1, 2, 3 },
{ "multitap2", 1, CTL_MP5, 1, 2, 3, 4 },
{ "nothingpluggedin2", 1, CTL_NONE, 0, 0, 0, 0}
};
for (auto &m : map)
if (!strcasecmp(m.name, name))
{
S9xSetController(m.port, m.controller, m.id1, m.id2, m.id3, m.id4);
break;
}
}
bool Snes9xWindow::event_key(GdkEventKey *event)
@ -543,12 +506,6 @@ bool Snes9xWindow::draw(const Cairo::RefPtr<Cairo::Context> &cr)
S9xDisplayRefresh();
if (!(config->fullscreen))
{
config->window_width = get_width();
config->window_height = get_height();
}
if ((is_paused() || NetPlay.Paused) && (gui_config->splash_image < SPLASH_IMAGE_STARFIELD || gui_config->rom_loaded))
{
S9xDeinitUpdate(last_width, last_height);
@ -563,7 +520,7 @@ void Snes9xWindow::focus_notify(bool state)
{
focused = state;
if (!state && config->pause_emulation_on_switch)
if (!state && config->pause_emulation_on_switch && !paused_from_focus_loss)
{
sys_pause++;
propagate_pause_state();
@ -946,19 +903,23 @@ const char *markup = _(R"(<b>Information for %s</b>
void Snes9xWindow::configure_widgets()
{
enable_widget("continue_item", config->rom_loaded);
enable_widget("pause_item", config->rom_loaded);
enable_widget("reset_item", config->rom_loaded);
enable_widget("load_state_item", config->rom_loaded);
enable_widget("save_state_item", config->rom_loaded);
enable_widget("save_spc_item", config->rom_loaded);
enable_widget("hard_reset_item", config->rom_loaded);
enable_widget("record_movie_item", config->rom_loaded);
enable_widget("stop_recording_item", config->rom_loaded);
enable_widget("open_movie_item", config->rom_loaded);
enable_widget("jump_to_frame_item", config->rom_loaded);
enable_widget("cheats_item", config->rom_loaded);
enable_widget("rom_info_item", config->rom_loaded);
auto enable_when_rom_loaded = {
"continue_item",
"pause_item",
"reset_item",
"load_state_item",
"save_state_item",
"save_spc_item",
"hard_reset_item",
"record_movie_item",
"stop_recording_item",
"open_movie_item",
"jump_to_frame_item",
"cheats_item",
"rom_info_item"
};
for (auto &widget : enable_when_rom_loaded)
enable_widget(widget, config->rom_loaded);
enable_widget("sync_clients_item",
config->rom_loaded &&
@ -1020,7 +981,7 @@ void Snes9xWindow::reset_screensaver()
GdkDisplay *gdk_display = window->get_display()->gobj();
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW(gdk_window))
if (is_x11())
{
XResetScreenSaver(GDK_DISPLAY_XDISPLAY(gdk_display));
}
@ -1091,7 +1052,7 @@ Snes9xWindow::get_refresh_rate()
#endif
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY(gdk_display))
if (is_x11())
{
Window xid = gdk_x11_window_get_xid(gdk_window);
Display *dpy = gdk_x11_display_get_xdisplay(gdk_display);
@ -1100,7 +1061,7 @@ Snes9xWindow::get_refresh_rate()
#endif
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY(gdk_display))
if (is_wayland())
{
GdkMonitor *monitor = gdk_display_get_monitor_at_window(gdk_display, gdk_window);
refresh_rate = (double)gdk_monitor_get_refresh_rate(monitor) / 1000.0;
@ -1139,70 +1100,78 @@ int Snes9xWindow::get_auto_input_rate()
return new_input_rate;
}
#ifdef GDK_WINDOWING_X11
static void set_bypass_compositor(Display *dpy, Window window, unsigned char bypass)
void Snes9xWindow::set_bypass_compositor(bool bypass)
{
uint32 value = bypass;
Atom net_wm_bypass_compositor = XInternAtom(dpy, "_NET_WM_BYPASS_COMPOSITOR", False);
XChangeProperty(dpy, window, net_wm_bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *)&value, 1);
}
#ifdef GDK_WINDOWING_X11
auto gdk_window = window->get_window()->gobj();
if (is_x11() && config->default_esc_behavior != ESC_TOGGLE_MENUBAR)
{
auto gdk_display = window->get_display()->gobj();
Display *dpy = gdk_x11_display_get_xdisplay(gdk_display);
Window window = gdk_x11_window_get_xid(gdk_window);
uint32 value = bypass;
Atom net_wm_bypass_compositor = XInternAtom(dpy, "_NET_WM_BYPASS_COMPOSITOR", False);
XChangeProperty(dpy, window, net_wm_bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *)&value, 1);
}
#endif
}
void Snes9xWindow::set_custom_video_mode(bool enable)
{
#ifdef GDK_WINDOWING_X11
GdkDisplay *gdk_display = window->get_display()->gobj();
GdkWindow *gdk_window = window->get_window()->gobj();
if (!is_x11())
return;
Display *dpy = gdk_x11_display_get_xdisplay(gdk_display);
RRMode id = 0;
if (enable)
id = config->xrr_screen_resources->modes[config->xrr_index].id;
else
id = config->xrr_crtc_info->mode;
if (XRRSetCrtcConfig(dpy,
config->xrr_screen_resources,
config->xrr_screen_resources->crtcs[0],
CurrentTime,
config->xrr_crtc_info->x,
config->xrr_crtc_info->y,
id,
config->xrr_crtc_info->rotation,
&config->xrr_crtc_info->outputs[0],
1) != 0)
{
config->change_display_resolution = 0;
}
if (gui_config->auto_input_rate)
{
Settings.SoundInputRate = top_level->get_auto_input_rate();
S9xUpdateDynamicRate(1, 2);
}
#endif
}
void Snes9xWindow::enter_fullscreen_mode()
{
int rom_loaded = config->rom_loaded;
bool rom_loaded = config->rom_loaded;
if (config->fullscreen)
return;
GdkDisplay *gdk_display = window->get_display()->gobj();
GdkWindow *gdk_window = window->get_window()->gobj();
config->rom_loaded = 0;
config->rom_loaded = false;
config->fullscreen = true;
nfs_width = config->window_width;
nfs_height = config->window_height;
int nfs_x;
int nfs_y;
window->get_position(nfs_x, nfs_y);
#ifdef GDK_WINDOWING_X11
if (config->change_display_resolution && GDK_IS_X11_WINDOW(gdk_window))
{
Display *dpy = gdk_x11_display_get_xdisplay(gdk_display);
auto xrr_screen_resources = XRRGetScreenResourcesCurrent(dpy, gdk_x11_window_get_xid(gdk_window));
auto xrr_crtc_info = XRRGetCrtcInfo(dpy,
xrr_screen_resources,
xrr_screen_resources->crtcs[0]);
gdk_display_sync(gdk_display);
if (XRRSetCrtcConfig(dpy,
xrr_screen_resources,
xrr_screen_resources->crtcs[0],
CurrentTime,
xrr_crtc_info->x,
xrr_crtc_info->y,
xrr_screen_resources->modes[config->xrr_index].id,
xrr_crtc_info->rotation,
&xrr_crtc_info->outputs[0],
1) != 0)
{
config->change_display_resolution = 0;
}
XRRFreeCrtcInfo(xrr_crtc_info);
XRRFreeScreenResources(xrr_screen_resources);
if (gui_config->auto_input_rate)
{
Settings.SoundInputRate = top_level->get_auto_input_rate();
S9xUpdateDynamicRate(1, 2);
}
}
#endif
if (config->change_display_resolution)
set_custom_video_mode(true);
/* Make sure everything is done synchronously */
GdkDisplay *gdk_display = window->get_display()->gobj();
gdk_display_sync(gdk_display);
window->fullscreen();
gdk_display_sync(gdk_display);
@ -1223,19 +1192,9 @@ void Snes9xWindow::enter_fullscreen_mode()
S9xDisplayRefresh();
}
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW(window->get_window()->gobj()) &&
config->default_esc_behavior != ESC_TOGGLE_MENUBAR)
{
set_bypass_compositor(gdk_x11_display_get_xdisplay(gdk_display),
gdk_x11_window_get_xid(gdk_window),
1);
}
#endif
config->fullscreen = 1;
set_bypass_compositor(true);
config->rom_loaded = rom_loaded;
/* If we're running a game, disable ui when entering fullscreen */
if (!Settings.Paused && config->rom_loaded)
config->ui_visible = false;
@ -1245,7 +1204,7 @@ void Snes9xWindow::enter_fullscreen_mode()
void Snes9xWindow::leave_fullscreen_mode()
{
int rom_loaded = config->rom_loaded;
bool rom_loaded = config->rom_loaded;
if (!config->fullscreen)
return;
@ -1260,74 +1219,30 @@ void Snes9xWindow::leave_fullscreen_mode()
S9xDisplayRefresh();
}
GdkDisplay *gdk_display = window->get_display()->gobj();
GdkWindow *gdk_window = window->get_window()->gobj();
config->rom_loaded = false;
config->rom_loaded = 0;
if (config->change_display_resolution)
set_custom_video_mode(false);
#ifdef GDK_WINDOWING_X11
if (config->change_display_resolution && GDK_IS_X11_WINDOW(gdk_window))
{
Display *dpy = gdk_x11_display_get_xdisplay(gdk_display);
if (config->xrr_index > config->xrr_screen_resources->nmode)
config->xrr_index = 0;
gdk_display_sync(gdk_display);
XRRSetCrtcConfig(dpy,
config->xrr_screen_resources,
config->xrr_screen_resources->crtcs[0],
CurrentTime,
config->xrr_crtc_info->x,
config->xrr_crtc_info->y,
config->xrr_crtc_info->mode,
config->xrr_crtc_info->rotation,
&config->xrr_crtc_info->outputs[0],
1);
if (gui_config->auto_input_rate)
{
Settings.SoundInputRate = top_level->get_auto_input_rate();
S9xUpdateDynamicRate(1, 2);
}
}
#endif
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(gdk_window))
{
S9xDeinitDisplay();
}
#endif
// If the window is covered by a subsurface, for some reason Gtk doesn't
// send any resize events or do anything to resize the window. So shrink
// the subsurface's viewport to 2x2 temporarily.
auto driver = S9xDisplayGetDriver();
if (driver)
driver->shrink();
window->unfullscreen();
window->show();
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW(gdk_window))
{
set_bypass_compositor(gdk_x11_display_get_xdisplay(gdk_display),
gdk_x11_window_get_xid(gdk_window),
0);
}
#endif
resize(nfs_width, nfs_height);
window->move(nfs_x, nfs_y);
set_bypass_compositor(false);
config->rom_loaded = rom_loaded;
config->fullscreen = 0;
config->fullscreen = false;
configure_widgets();
window->show();
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(gdk_window))
{
S9xReinitDisplay();
}
#endif
if (driver)
driver->regrow();
}
void Snes9xWindow::resize_viewport(int width, int height)
@ -1359,11 +1274,11 @@ void Snes9xWindow::center_mouse()
int y;
window->get_window()->get_origin(x, y);
int w = window->get_width();
int h = window->get_height();
int half_w = window->get_width() / 2;
int half_h = window->get_height() / 2;
gdk_mouse_x = x + w / 2;
gdk_mouse_y = y + h / 2;
gdk_mouse_x = x + half_w;
gdk_mouse_y = y + half_h;
window->get_display()->get_default_seat()->get_pointer()->warp(window->get_screen(), gdk_mouse_x, gdk_mouse_y);
}

View file

@ -39,6 +39,8 @@ class Snes9xWindow : public GtkBuilderWindow
void leave_fullscreen_mode();
void toggle_fullscreen_mode();
void finish_fullscreen();
void set_bypass_compositor(bool bypass);
void set_custom_video_mode(bool enable);
/* Cursor modifying functions */
void show_mouse_cursor();
@ -88,7 +90,6 @@ class Snes9xWindow : public GtkBuilderWindow
int last_width, last_height;
int mouse_region_x, mouse_region_y;
int mouse_region_width, mouse_region_height;
int nfs_width, nfs_height, nfs_x, nfs_y;
int autovrr_saved_frameskip;
int autovrr_saved_sound_input_rate;
bool autovrr_saved_sync_to_vblank;

View file

@ -11,7 +11,7 @@
#include "gtk_s9x.h"
#include "gtk_display.h"
#include "gtk_shader_parameters.h"
#include "shaders/glsl.h"
#include "common/video/opengl/shaders/glsl.h"
#include "gfx.h"

View file

@ -4,7 +4,6 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <errno.h>
#include <unistd.h>
#include "gtk_s9x.h"

View file

@ -1156,6 +1156,8 @@ bool retro_load_game(const struct retro_game_info *game)
if (!rom_loaded && log_cb)
log_cb(RETRO_LOG_ERROR, "ROM loading failed...\n");
Memory.ClearSRAM();
return rom_loaded;
}

View file

@ -45,7 +45,6 @@ NSWindowFrameAutosaveName const kCheatFinderWindowIdentifier = @"s9xCheatFinderW
gameWindow.contentView.layer.backgroundColor = NSColor.blackColor.CGColor;
gameWindow.title = @"Snes9x";
gameWindow.restorationClass = [self class];
gameWindow.frameAutosaveName = kMainWindowIdentifier;
gameWindow.releasedWhenClosed = NO;
gameWindow.backgroundColor = NSColor.clearColor;
@ -660,7 +659,6 @@ NSWindowFrameAutosaveName const kCheatFinderWindowIdentifier = @"s9xCheatFinderW
window = self.cheatsWindowController.window;
window.title = NSLocalizedString(@"Cheats", nil);
window.restorationClass = self.class;
window.frameAutosaveName = kCheatsWindowIdentifier;
window.releasedWhenClosed = NO;
@ -695,7 +693,6 @@ NSWindowFrameAutosaveName const kCheatFinderWindowIdentifier = @"s9xCheatFinderW
window = self.cheatFinderWindowController.window;
window.title = NSLocalizedString(@"Cheat Finder", nil);
window.restorationClass = self.class;
window.frameAutosaveName = kCheatFinderWindowIdentifier;
window.releasedWhenClosed = NO;

View file

@ -131,7 +131,7 @@ bool SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint32 ind
void ClearButtonCodeForJoypad(uint32 vendorID, uint32 productID, uint32 index, S9xButtonCode buttonCode);
void ClearJoypad(uint32 vendorID, uint32 productID, uint32 index);
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJuypadButtons(uint32 vendorID, uint32 productID, uint32 index);
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJoypadButtons(uint32 vendorID, uint32 productID, uint32 index);
std::string LabelForInput(uint32 vendorID, uint32 productID, uint32 cookie, int32 value);

View file

@ -684,7 +684,7 @@ void ClearJoypad(uint32 vendorID, uint32 productID, uint32 index)
}
}
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJuypadButtons(uint32 vendorID, uint32 productID, uint32 index)
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJoypadButtons(uint32 vendorID, uint32 productID, uint32 index)
{
struct JoypadDevice device;
device.vendorID = vendorID;
@ -712,7 +712,50 @@ void SetUpHID (void)
{
IOHIDManagerRegisterInputValueCallback(hidManager, gamepadAction, NULL);
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
IOHIDManagerSetDeviceMatching(hidManager, NULL);
CFMutableArrayRef matching = CFArrayCreateMutable(kCFAllocatorDefault, 4, &kCFTypeArrayCallBacks);
uint32 page = kHIDPage_GenericDesktop;
uint32 usage = kHIDUsage_GD_Joystick;
CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
CFMutableDictionaryRef entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsagePageKey), (void *)pageRef);
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsageKey), (void *)usageRef);
CFArrayAppendValue(matching, entry);
CFRelease(usageRef);
CFRelease(entry);
usage = kHIDUsage_GD_GamePad;
usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsagePageKey), (void *)pageRef);
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsageKey), (void *)usageRef);
CFArrayAppendValue(matching, entry);
CFRelease(usageRef);
CFRelease(entry);
usage = kHIDUsage_GD_MultiAxisController;
usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsagePageKey), (void *)pageRef);
CFDictionarySetValue(entry, CFSTR(kIOHIDDeviceUsageKey), (void *)usageRef);
CFArrayAppendValue(matching, entry);
CFRelease(usageRef);
CFRelease(pageRef);
CFRelease(entry);
uint32 vendor = 0x28DE; // Valve, apparently
CFNumberRef vendorRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(entry, CFSTR(kIOHIDVendorIDKey), (void *)pageRef);
CFArrayAppendValue(matching, entry);
CFRelease(vendorRef);
CFRelease(entry);
IOHIDManagerSetDeviceMatchingMultiple(hidManager, matching);
CFRelease(matching);
ParseDefaults();

View file

@ -3286,7 +3286,7 @@ void QuitWithFatalError ( NSString *message)
{
pthread_mutex_lock(&keyLock);
NSMutableArray<S9xJoypadInput *> *inputs = [NSMutableArray new];
std::unordered_map<struct JoypadInput, S9xButtonCode> buttonCodeMap = GetJuypadButtons(vendorID, productID, index);
std::unordered_map<struct JoypadInput, S9xButtonCode> buttonCodeMap = GetJoypadButtons(vendorID, productID, index);
for (auto it = buttonCodeMap.begin(); it != buttonCodeMap.end(); ++it)
{
S9xJoypadInput *input = [S9xJoypadInput new];

Some files were not shown because too many files have changed in this diff Show more