Compare commits

..

17 Commits
v075 ... v076

Author SHA1 Message Date
Tim Allen
64072325c4 Update to v076 release.
byuu says:

Most notable in this release is that sound support has been added to my
own Super Game Boy emulation. The GUI toolkit, phoenix, has also
received a complete rewrite; with the most visible change there being
that windows are now resizable.

Changelog (since v075):
* added sound emulation to Game Boy core
* fixed Super Game Boy save state
* support added HexEdit widget to Windows and Qt targets; debugger can
  now be compiled on all platforms
* entering fullscreen now auto-hides mouse; and mouse capture is toggled
  otherwise by F12 key
* fullscreen command and geometry caching works much better on GTK+ and
  Qt targets
* phoenix rewritten from scratch; now supports resizable layout
  containers
* phoenix/Windows no longer relies on buggy SetParent API to reparent
  widgets
2011-02-27 20:11:01 +11:00
Tim Allen
017f9926fc Update to v075r16 release.
byuu says:

This has my latest API enhancements, but there are some known issues:
- resize on Windows seems to not repaint the buttons properly in rare
  cases. I may just need to revert to flickering resize.
- GTK+ reports the wrong menu height, off by two pixels, prior to the
  window being realized (made visible)
  - this results in the main window moving up two pixels after each run
    of bsnes

The menu height bug was actually there previously, it was just that Qt
and GTK+ were computing the frame margins incorrectly (ignoring the menu
bar) before.

On the bright side, ui/settings/input.cpp has been improved by way of
the new multi-layout support. The window is no longer forced to an
awkward 640 pixels wide, as the mouse axes/buttons can overlap now. The
code is also simpler since I am using the Layout::setVisible command to
toggle groups on and off instead of doing it for each and every control.
2011-02-27 20:05:10 +11:00
Tim Allen
c31543ea58 Update to v075r15 release.
byuu says:

phoenix/GTK+ rewrite completed. All three targets should now be 100%
operational with full resize support.
2011-02-24 20:27:21 +11:00
Tim Allen
7c3aaf12b0 Update to v075r14 release.
byuu says:

Adds the new phoenix/Windows. Testing would once again be appreciated,
as this is basically a rewrite of the entire core of the GUI.
2011-02-24 20:25:20 +11:00
Tim Allen
3fad0a0105 Update to v075r13 release.
byuu says (in the thread about rewriting Phoenix):

- added phoenix/reference; a dummy implementation that contains the 22KB
  of static boilerplate code needed to start a new target
- OS::setDefaultFont removed; problem is that objects in the global
  scope are constructed before you can call that function
- added Window::setWidgetFont, which is applied if your widgets have no
  custom font set already upon attaching them to the window with
  Window::setLayout, more understandable behavior
- renamed ListBox to ListView

byuu says (in the v075 WIP thread):

Found the source of lag on cartridge load. ListView::modify() was not
locking Qt messages, so it was causing a CheatEditor::refresh (copies
16MB of memory each call) for all 128 cheat items.

Final issue now is that nested submenus (menus inside of menus) are not
applying Window::setMenuFont yet.
2011-02-16 23:35:40 +11:00
Tim Allen
72a2967eeb Update to v075r12 release.
byuu says:

phoenix has been completely rewritten from scratch, and bsnes/ui + bsnes/ui-gameboy have been updated to use the new API. Debugger works too. Currently, only phoenix/Qt is completed, and there are two known issues:

1: font sizes of menu items are wrong, I can fix this easily enough
2: there's some sort of multi-second lag when loading games, not sure
   what's happening there yet

The new phoenix isn't exactly complete yet, still making some key
changes, and then I'll start on phoenix/Windows and phoenix/GTK+.

The most noticeable difference is that you don't have to give all of the
header paths and PHOENIX_PLATFORM defines when compiling individual GUI
object files. It's only needed for phoenix.cpp itself. The overall
structure of the phoenix source folder is much saner as well for
sync.sh.

I'm really surprised things are working as well as they are for
a two-day power rewrite of an entire phoenix target. The other targets
won't be as bad insofar as the core stuff is completed this time. And
thank god for that, I was about ready to kill myself after writing
dozens of lines like this:

    HorizontalSlider::HorizontalSlider() : state(*new State),
    base_from_member<pHorizontalSlider&>(*new pHorizontalSlider(*this)),
    Widget(base_from_member<pHorizontalSlider&>::value),
    p(base_from_member<pHorizontalSlider&>::value) {}

But each platform does have some new, unique problems. phoenix/GTK+ was
acting screwy prior to the rewrite, and will most likely still have
issues. Even more important, one of the major points of this rewrite was
having the new phoenix/core cache widget settings/data, so that I can
destroy and recreate widgets rather than relying on SetParent. This
means that simple copying of the old phoenix/Windows won't work, and
this new method is significantly more involved.
2011-02-15 23:22:37 +11:00
Tim Allen
a8ee35633c Update to v075r11 release.
byuu says:

Rewrote the way menus are attached, they act like layouts/widgets now.

All three phoenix targets once again work with both radio menu items and
radio widgets. Both GTK+ and Qt have built-in group controls right
inside the widgets, so I don't have to keep my own groups around
anymore. They do act screwy at widget creation though, have to jump
through some hoops to get it to work right. All I can say is, definitely
set all child widgets to the parent before trying to check any of them.

My long-term goal for the main window is to honor the fullscreen video
setting as a generic setting, and let the window scale auto-fit the best
possible size that matches your scale preference into the output window,
centered just like fullscreen. For now, I've just set it to a fixed
window size until I finish working on phoenix. The scale X settings will
just be to snap the window to an exact size in case you don't want any
black borders, they won't be radio items and the bsnes-geometry.cfg file
will save width/height information as well.

Simplified the sizing requirements for creating layouts and updated all
bsnes windows to support the new system. Layouts also expose their
minimum width/height values, which I use to create perfectly sized
windows on all three platforms. This will fix cut-off heights on the
last Windows WIP. Qt is being annoying though and forcing a minimum
window size of 300,100 despite me telling it to use a smaller window
size. Always have to fight with Qt, I swear to god.
2011-02-10 21:08:12 +11:00
Tim Allen
7dda70baa4 Update to v075r10 release.
byuu says:

phoenix/Windows and phoenix/Qt are mostly fully operational now. All
platforms support dynamic layout resizing. I tried WM_GETMINMAXINFO
(thanks, OV2), but it was acting kind of choppy on resize, and it would
get confused and go crazy if you snapped one direction to the minimum
height but not another, so for now I'm leaving it off.

phoenix/GTK+ will be missing some functionality in regards to window
geometry. The other two have a more coherent strategy now: geometry() is
the client area, and setGeometry moves the client area to where you ask
for. This makes truly centering your client area trivial.
frameGeometry() includes the borders, menu and status. There is no
setFrameGeometry(), not sure if I really even want that, but it could be
useful so who knows. All targets also support non-resizable windows.

X11 is of course horrendously poor with frame sizes, Qt and GTK+ don't
even pretend to simulate them, so they say the frame is 0x0 pixels in
size until your widget is fully realized and visible to the end user. So
for now, to get window positioning right, I have to wait until the
window appears and then reposition the window again, causing a slight
jump. My plan is to build some persistent caching support directly into
phoenix. From here, I can just have the window snap the very first time
you run your very first phoenix app. I'll then determine the frame size
information, and use that to create future windows. Once they spawn,
I'll recheck and update the frame size info in case it has changed (eg
user changed themes.) Saving settings into .config/phoenix will allow me
to avoid having to snap the window every time on first startup. If the
config file is missing or unwritable, too bad, happens every time then.

I'm thinking about renaming onResize to onSize, and getting rid of
Window::create(). Rather make it spawn like every other control in its
constructor.
2011-02-07 20:20:07 +11:00
Tim Allen
2c61ce2522 Update to v075r09 release.
byuu says:

Ported phoenix/Windows and phoenix/GTK+ over to the new system. There
are some problems that need to be addressed:

- Windows ComboBox height setting needs widget creation height to
  properly scale itself (make Widget::setGeometry virtual and override
  ComboBox::setGeometry)
- Windows Canvas is completely broken
- GTK+ Canvas is slow as shit compared to Qt Canvas, probably nothing
  I can do about it, have to do a very costly conversion because GTK+ is
  stupid and uses BGR like Nintendo
- GTK+ listboxes are fucking insanely complicated to set up. Currently
  I just split the second-half of creation to the setHeaderText call,
  but when you don't call that, things explode
  - I'm probably going to have to completely destroy and recreate
    listboxes when changing the header text / column count
- Qt resize code is still impossible to get right, it's not letting me
  size a window > 2/3rds of the screen size (it's in their docs)
  - I swear, Qt is the most painful API in the world to move/size
    windows with
- now that Window is separate, it really needs geometry() and
  frameGeometry() as the two are quite different
- I need a way to toggle window resizability for fixed layouts, Qt is
  once again going to be a nightmare as it lacks a way to do this other
  than fixed layouts
- GTK+ currently explodes on bsnes, millions of console messages,
  wonderful
- plenty more I'm forgetting

One bit of really cool/good news though: I made
Fixed/Horizontal/Vertical layouts external to phoenix itself. The code
is included for all targets so that it's always there and compiled into
one object, but the great news is that you can easily write your own
layout widgets and they'll work on all toolkits instantly.

That resize issue with bsnes was so simple it's annoying: my FixedLayout
container was repositioning on geometry updates. Made it only do it once
at creation like it should.

bsnes now has a fancy resize, grow the window and get black borders,
shrink it and the video shrinks with it. I plan to make it fancier with
constraint settings (center, scale, stretch). Basically I want to turn
the fullscreen setting into a general setting that also applies to
windowed scaling. I will probably turn the video scale X sizes into
regular items instead of radio boxes, so you can easily reset to a fixed
size whenever you want. Update bsnes to remember width,height geometry
as well and it should be quite nice.
2011-02-07 20:18:01 +11:00
Tim Allen
266495b475 Update to v075r08 release.
byuu says:

Eleven hours of work. Window is now a base type (inherits from Object,
not Widget), same for Layout. FixedLayout still inherits from Layout.
Added HorizontalLayout and VerticalLayout types, that can append each
other to themselves to create box layouts. Layout margins are supported,
spacing is specified inline (I find this a much better way to fine-grain
spacing than Qt's single setSpacing function), and alignment is handled
strictly via padding widgets (insert a zero-sized label and it will
automatically grow to consume all extra space.)

Overall, my box packing model is slightly less powerful than Qt's, but
it is a good deal simpler and and easier to use in 90% of cases. The one
limitation I hit was with my input settings window, I'm not currently
able to embed two different layouts and toggle one on and the other off
to show only either { mouse x-axis, y-axis } or { mouse left, middle,
right }, so they instead just space out differently and I had to grow
the input window width a bit to compensate.

Resizing works great, pretty cool seeing that this is the first time
I've ever written my own resizer. I had to fight with Qt for several
hours to the point of potentially developing an aneurysm, but I finally
got it to properly handle geometry and sizing stuff. Some weird issue
with the bsnes viewport widget, I tell it to resize and for some reason
it doesn't. Cheap hack, I just make it constantly resize every video
refresh and it eventually takes. Wish I knew what was up with that.

All of bsnes now uses dynamic layouts sans the main window, so you can
resize them however you like.

This is still all Qt-only, I'm afraid. The other two ports are
in-progress.
2011-02-07 20:15:43 +11:00
Tim Allen
133d568f76 Update to v075r07 release.
byuu says:

This has the phoenix changes applied. Instead of widgets attaching
directly to windows, you now attach them to layouts, which can then be
attached to windows. Layouts are widgets themselves, so adding layouts
to layouts is trivial. It also allows for multi-widget show/hide, etc.

Right now there is only FixedLayout, but of course the plan is to
support a BoxLayout, that lets you add HorizontalLayout and
VerticalLayout containers to it, thus enabling auto-resize and simpler
form layout.

So far only phoenix/Qt is 100% moved over. phoenix/GTK+ has about 1/3rd
ported, and phoenix/Windows only has one control ported over as
a proof-of-concept.

On the user side, bsnes, bgameboy, snespurify and curse have been moved
to this new layout system. All of bsnes works great with it, as far as
I can tell. Fullscreen, debugger, etc are good.
2011-02-07 20:14:14 +11:00
Tim Allen
b433838e9f Update to v075r06 release.
byuu says:

Removed the floating-point volume adjustments from the wave channel and
the left/right speaker mixers. Also, against my better judgment I'm
backing out of left/right computation when they are both turned off.
This basically makes non-stereo games run faster, but will make stereo
games appear to run slower. I don't like it when end-users experience
mystery slowdowns.

Anyway, it appears that the audio calculation is really fucking
demanding. Knocks FPS from 800 down to 300. I thought it might be libco,
so I took it out and it only went up to 305fps o.O

There is also some sort of problem with bsnes/Super Game Boy audio. The
latency is really great when you first start, but it seems to drift
apart over time until it is well over 500ms, and then it either pops or
fades back to very low, sub-50ms latency again. The way I handle mixing
is that the coprocessor audio samples go into a resampler to the native
SNES rate, and fed to an output buffer. SNES audio samples go there
untouched. When there is a sample in each, I add them together and
average the result (I still don't understand why we divide by two since
these are signed integers), and output it immediately. It's just-in-time
sampling, so as long as DSP v Coprocessor do not drift very far, it
should have very low latency. And I make the CPU sync DSP and
Coprocessor once per scanline, which is something like 15 samples or so.
2011-02-03 22:17:35 +11:00
Tim Allen
a3abe8ebaa Update to v075r05 release.
byuu says:

Added Game Boy sound emulation, all four channels.
It's really, really, really bad. Plenty of bugs, I don't even know what
the fuck a high-pass filter is so that isn't there. Hermite resampling
from 4MHz down to 44KHz. But it's tolerable.
I don't understand what sweep is for at all, and I'm sure I have that
insane recursive reload behavior wrong.

This is pretty much my own design. I referenced blargg's gb snd emu,
blargg's older gb apu ref, Cydrak's APU core, that lousy gbdev wiki
article, the completely and utterly worthless pandocs, and received
nothing but bad and wrong information that just wasted my time from

But I managed to pull it off. It's also painfully slow, like 250fps on
my machine slow. Countless optimizations are possible.
2011-02-02 21:38:28 +11:00
Tim Allen
f88ef9e9a2 Update to v075r04 release.
byuu says:

Changelog:
- hooked up everything necessary for Game Boy sound emulation ...
- bgameboy and bsnes/SGB input 4MHz frequency, and output 44.1KHz
  frequency (produces soft static for now, to verify it is working)
- rewrote all of gameboy/apu, it now has a 4MHz worker thread, and
  separate classes/folders for each channel+master, and serializes

So it's basically all I can do without actual emulation code or
human-readable documentation/example code.
2011-02-02 21:37:31 +11:00
Tim Allen
a136378a7b Update to v075r03 release.
byuu says:

Changelog:
- added full HexEditor widget to phoenix/Qt (has dynamic scrollbar like
  phoenix/GTK, but does not yet support page up/down scrolling)
- optimized debugger to look great with either phoenix/GTK or phoenix/Qt
- fixed phoenix/Qt fullscreen mode (had to allow resizing of the layout,
  and resize the container)
- fixed phoenix/Qt Window::setBackgroundColor() bug that was making
  statusbar invisible
- entering fullscreen now captures mouse, leaving fullscreen releases it
  - so by default, no cursor in fullscreen mode now
- F12 key was assigned the task of toggling mouse capture,
  Tools->Capture Mouse was removed
- above change allows toggling mouse capture in fullscreen if you like

It wasn't my idea, but toggling the mouse capture in fullscreen also hiding the mouse cursor is what I call genius design. Two birds with one stone, and very intuitive.

Also, the default GUI on Linux for bsnes and bgameboy is now Qt, instead
of GTK+. I did this because Qt's fullscreen is far more stable, and
I fixed up the remaining bugs anyway.
2011-02-02 21:35:15 +11:00
Tim Allen
012cdd4b14 Update to v075r02 release.
byuu says:

Changelog:
- added ui-libsnes directory back into source archive; make archive-all
  includes it now
- added basic HexEditor widget to phoenix/Windows
2011-02-02 21:33:35 +11:00
Tim Allen
eecc085e42 Update to v075r01 release.
byuu says:

Changelog:
- fixed Super Game Boy save state support
- both SNES and GameBoy only initialize serialize size on cartridge load
  once now, just like I've already done with memory mapping
- added nall/public_cast.hpp for fun ... don't worry, I'm never actually
  going to use it in production code :D
2011-01-29 20:48:44 +11:00
464 changed files with 77112 additions and 69103 deletions

View File

@@ -65,6 +65,6 @@ clean:
-@$(call delete,*.manifest)
archive-all:
tar -cjf bsnes.tar.bz2 data gameboy libco nall obj out phoenix ruby snes ui ui-gameboy Makefile cc.bat clean.bat sync.sh
tar -cjf bsnes.tar.bz2 data gameboy libco nall obj out phoenix ruby snes ui ui-gameboy ui-libsnes Makefile cc.bat clean.bat sync.sh
help:;

File diff suppressed because it is too large Load Diff

View File

@@ -3,83 +3,103 @@
#define APU_CPP
namespace GameBoy {
#include "mmio/mmio.cpp"
#include "square1/square1.cpp"
#include "square2/square2.cpp"
#include "wave/wave.cpp"
#include "noise/noise.cpp"
#include "master/master.cpp"
#include "serialization.cpp"
APU apu;
void APU::Main() {
apu.main();
}
void APU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(sequencer_base == 0) { //512hz
if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz
square1.clock_length();
square2.clock_length();
wave.clock_length();
noise.clock_length();
}
if(sequencer_step == 2 || sequencer_step == 6) { //128hz
square1.clock_sweep();
}
if(sequencer_step == 7) { //64hz
square1.clock_envelope();
square2.clock_envelope();
noise.clock_envelope();
}
sequencer_step++;
}
sequencer_base++;
square1.run();
square2.run();
wave.run();
noise.run();
master.run();
system.interface->audio_sample(master.center, master.left, master.right);
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
void APU::power() {
create(Main, 4194304);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
channel1.sweep_time = 0;
channel1.sweep_direction = 0;
channel1.sweep_shift = 0;
foreach(n, mmio_data) n = 0x00;
sequencer_base = 0;
sequencer_step = 0;
channel1.wave_pattern_duty = 0;
channel1.sound_length = 0;
square1.power();
square2.power();
wave.power();
noise.power();
master.power();
}
channel1.initial_envelope_volume = 0;
channel1.envelope_direction = 0;
channel1.envelope_sweep = 0;
uint8 APU::mmio_read(uint16 addr) {
static const uint8 table[48] = {
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave
0xff, 0xff, 0x00, 0x00, 0xbf, //noise
0x00, 0x00, 0x70, //master
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
};
channel1.frequency = 0;
channel1.initialize = 0;
channel1.consecutive_selection = 0;
if(addr == 0xff26) {
uint8 data = master.enable << 7;
if(square1.counter && square1.length) data |= 0x01;
if(square2.counter && square2.length) data |= 0x02;
if( wave.counter && wave.length) data |= 0x04;
if( noise.counter && noise.length) data |= 0x08;
return data | table[addr - 0xff10];
}
channel2.wave_pattern_duty = 0;
channel2.sound_length = 0;
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
return 0xff;
}
channel2.initial_envelope_volume = 0;
channel2.envelope_direction = 0;
channel2.envelope_sweep = 0;
void APU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
channel2.frequency = 0;
channel2.initialize = 0;
channel2.consecutive_selection = 0;
channel3.off = 0;
channel3.sound_length = 0;
channel3.output_level = 0;
channel3.frequency = 0;
channel3.initialize = 0;
channel3.consecutive_selection = 0;
for(unsigned n = 0; n < 16; n++) channel3.pattern[n] = 0;
channel4.sound_length = 0;
channel4.initial_envelope_volume = 0;
channel4.envelope_direction = 0;
channel4.envelope_sweep = 0;
channel4.shift_clock_frequency = 0;
channel4.counter_step_width = 0;
channel4.dividing_ratio = 0;
channel4.initialize = 0;
channel4.consecutive_selection = 0;
control.output_vin_to_so2 = 0;
control.so2_output_level = 0;
control.output_vin_to_so1 = 0;
control.so1_output_level = 0;
control.output_channel4_to_so2 = 0;
control.output_channel3_to_so2 = 0;
control.output_channel2_to_so2 = 0;
control.output_channel1_to_so2 = 0;
control.output_channel4_to_so1 = 0;
control.output_channel3_to_so1 = 0;
control.output_channel2_to_so1 = 0;
control.output_channel1_to_so1 = 0;
control.sound_on = 0;
control.channel4_on = 0;
control.channel3_on = 0;
control.channel2_on = 0;
control.channel1_on = 0;
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
}
}

View File

@@ -1,8 +1,27 @@
struct APU : Processor, MMIO {
#include "mmio/mmio.hpp"
#include "square1/square1.hpp"
#include "square2/square2.hpp"
#include "wave/wave.hpp"
#include "noise/noise.hpp"
#include "master/master.hpp"
uint8 mmio_data[48];
uint13 sequencer_base;
uint3 sequencer_step;
Square1 square1;
Square2 square2;
Wave wave;
Noise noise;
Master master;
static void Main();
void main();
void power();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void serialize(serializer&);
};

View File

@@ -0,0 +1,132 @@
#ifdef APU_CPP
void APU::Master::run() {
if(enable == false) {
center = 0;
left = 0;
right = 0;
return;
}
signed sample = 0, channels;
sample += apu.square1.output;
sample += apu.square2.output;
sample += apu.wave.output;
sample += apu.noise.output;
sample >>= 2;
center = sclamp<16>(sample);
if(left_enable == false && right_enable == false) {
left = center;
right = center;
return;
}
sample = 0;
channels = 0;
if(channel1_left_enable) { sample += apu.square1.output; channels++; }
if(channel2_left_enable) { sample += apu.square2.output; channels++; }
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
left = sclamp<16>(sample);
switch(left_volume) {
case 0: left >>= 3; break; // 12.5%
case 1: left >>= 2; break; // 25.0%
case 2: left = (left >> 2) + (left >> 3); break; // 37.5%
case 3: left >>= 1; break; // 50.0%
case 4: left = (left >> 1) + (left >> 3); break; // 62.5%
case 5: left -= (left >> 2); break; // 75.0%
case 6: left -= (left >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(left_enable == false) left = 0;
sample = 0;
channels = 0;
if(channel1_right_enable) { sample += apu.square1.output; channels++; }
if(channel2_right_enable) { sample += apu.square2.output; channels++; }
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
right = sclamp<16>(sample);
switch(right_volume) {
case 0: right >>= 3; break; // 12.5%
case 1: right >>= 2; break; // 25.0%
case 2: right = (right >> 2) + (right >> 3); break; // 37.5%
case 3: right >>= 1; break; // 50.0%
case 4: right = (right >> 1) + (right >> 3); break; // 62.5%
case 5: right -= (right >> 2); break; // 75.0%
case 6: right -= (right >> 3); break; // 87.5%
//case 7: break; //100.0%
}
if(right_enable == false) right = 0;
}
void APU::Master::write(unsigned r, uint8 data) {
if(r == 0) {
left_enable = data & 0x80;
left_volume = (data >> 4) & 7;
right_enable = data & 0x08;
right_volume = (data >> 0) & 7;
}
if(r == 1) {
channel4_left_enable = data & 0x80;
channel3_left_enable = data & 0x40;
channel2_left_enable = data & 0x20;
channel1_left_enable = data & 0x10;
channel4_right_enable = data & 0x08;
channel3_right_enable = data & 0x04;
channel2_right_enable = data & 0x02;
channel1_right_enable = data & 0x01;
}
if(r == 2) {
enable = data & 0x80;
}
}
void APU::Master::power() {
left_enable = 0;
left_volume = 0;
right_enable = 0;
right_volume = 0;
channel4_left_enable = 0;
channel3_left_enable = 0;
channel2_left_enable = 0;
channel1_left_enable = 0;
channel4_right_enable = 0;
channel3_right_enable = 0;
channel2_right_enable = 0;
channel1_right_enable = 0;
enable = 0;
center = 0;
left = 0;
right = 0;
}
void APU::Master::serialize(serializer &s) {
s.integer(left_enable);
s.integer(left_volume);
s.integer(right_enable);
s.integer(right_volume);
s.integer(channel4_left_enable);
s.integer(channel3_left_enable);
s.integer(channel2_left_enable);
s.integer(channel1_left_enable);
s.integer(channel4_right_enable);
s.integer(channel3_right_enable);
s.integer(channel2_right_enable);
s.integer(channel1_right_enable);
s.integer(enable);
s.integer(center);
s.integer(left);
s.integer(right);
}
#endif

View File

@@ -0,0 +1,24 @@
struct Master {
bool left_enable;
unsigned left_volume;
bool right_enable;
unsigned right_volume;
bool channel4_left_enable;
bool channel3_left_enable;
bool channel2_left_enable;
bool channel1_left_enable;
bool channel4_right_enable;
bool channel3_right_enable;
bool channel2_right_enable;
bool channel1_right_enable;
bool enable;
int16 center;
int16 left;
int16 right;
void run();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -1,248 +0,0 @@
#ifdef APU_CPP
uint8 APU::mmio_read(uint16 addr) {
if(addr == 0xff10) { //NR10
return (channel1.sweep_time << 4)
| (channel1.sweep_direction << 3)
| (channel1.sweep_shift << 0);
}
if(addr == 0xff11) { //NR11
return (channel1.wave_pattern_duty << 6);
}
if(addr == 0xff12) { //NR12
return (channel1.initial_envelope_volume << 4)
| (channel1.envelope_direction << 3)
| (channel1.envelope_sweep << 0);
}
if(addr == 0xff14) { //NR14
return (channel1.consecutive_selection << 6);
}
if(addr == 0xff16) { //NR21
return (channel2.wave_pattern_duty << 6);
}
if(addr == 0xff17) { //NR22
return (channel2.initial_envelope_volume << 4)
| (channel2.envelope_direction << 3)
| (channel2.envelope_sweep << 0);
}
if(addr == 0xff19) { //NR24
return (channel2.consecutive_selection << 6);
}
if(addr == 0xff1a) { //NR30
return (channel3.off << 7);
}
if(addr == 0xff1b) { //NR31
return (channel3.sound_length << 0);
}
if(addr == 0xff1c) { //NR32
return (channel3.output_level << 5);
}
if(addr == 0xff1e) { //NR34
return (channel3.consecutive_selection << 6);
}
if(addr == 0xff20) { //NR41
return (channel4.sound_length << 0);
}
if(addr == 0xff21) { //NR42
return (channel4.initial_envelope_volume << 4)
| (channel4.envelope_direction << 3)
| (channel4.envelope_sweep << 0);
}
if(addr == 0xff22) { //NR43
return (channel4.shift_clock_frequency << 4)
| (channel4.counter_step_width << 3)
| (channel4.dividing_ratio << 0);
}
if(addr == 0xff23) { //NR44
return (channel4.consecutive_selection << 6);
}
if(addr == 0xff24) { //NR50
return (control.output_vin_to_so2 << 7)
| (control.so2_output_level << 4)
| (control.output_vin_to_so1 << 3)
| (control.so1_output_level << 0);
}
if(addr == 0xff25) { //NR51
return (control.output_channel4_to_so2 << 7)
| (control.output_channel3_to_so2 << 6)
| (control.output_channel2_to_so2 << 5)
| (control.output_channel1_to_so2 << 4)
| (control.output_channel4_to_so1 << 3)
| (control.output_channel3_to_so1 << 2)
| (control.output_channel2_to_so1 << 1)
| (control.output_channel1_to_so1 << 0);
}
if(addr == 0xff26) { //NR52
return (control.sound_on << 7);
}
if(addr >= 0xff30 && addr <= 0xff3f) {
return channel3.pattern[addr & 15];
}
return 0x00;
}
void APU::mmio_write(uint16 addr, uint8 data) {
if(addr == 0xff10) { //NR10
channel1.sweep_time = (data >> 4) & 7;
channel1.sweep_direction = data & 0x08;
channel1.sweep_shift = data & 0x07;
return;
}
if(addr == 0xff11) { //NR11
channel1.wave_pattern_duty = (data >> 6) & 3;
channel1.sound_length = data & 0x3f;
return;
}
if(addr == 0xff12) { //NR12
channel1.initial_envelope_volume = (data >> 4) & 15;
channel1.envelope_direction = data & 0x08;
channel1.envelope_sweep = data & 0x07;
return;
}
if(addr == 0xff13) { //NR13
channel1.frequency = (channel1.frequency & 0x0700) | (data << 0);
return;
}
if(addr == 0xff14) { //NR14
channel1.initialize = data & 0x80;
channel1.consecutive_selection = data & 0x40;
channel1.frequency = ((data & 7) << 8) | (channel1.frequency & 0x00ff);
return;
}
if(addr == 0xff16) { //NR21
channel2.wave_pattern_duty = (data >> 6) & 3;
channel2.sound_length = data & 0x3f;
return;
}
if(addr == 0xff17) { //NR22
channel2.initial_envelope_volume = (data >> 4) & 15;
channel2.envelope_direction = data & 0x08;
channel2.envelope_sweep = data & 0x07;
return;
}
if(addr == 0xff18) { //NR23
channel2.frequency = (channel2.frequency & 0x0700) | (data << 0);
return;
}
if(addr == 0xff19) { //NR24
channel2.initialize = data & 0x80;
channel2.consecutive_selection = data & 0x40;
channel2.frequency = ((data & 7) << 8) | (channel2.frequency & 0x00ff);
return;
}
if(addr == 0xff1a) { //NR30
channel3.off = data & 0x80;
return;
}
if(addr == 0xff1b) { //NR31
channel3.sound_length = data;
return;
}
if(addr == 0xff1c) { //NR32
channel3.output_level = (data >> 5) & 3;
return;
}
if(addr == 0xff1d) { //NR33
channel3.frequency = (channel3.frequency & 0x0700) | (data << 0);
return;
}
if(addr == 0xff1e) { //NR34
channel3.initialize = data & 0x80;
channel3.consecutive_selection = data & 0x40;
channel3.frequency = ((data & 7) << 8) | (channel3.frequency & 0x00ff);
return;
}
if(addr == 0xff20) { //NR41
channel4.sound_length = data & 0x3f;
return;
}
if(addr == 0xff21) { //NR42
channel4.initial_envelope_volume = (data >> 3) & 15;
channel4.envelope_direction = data & 0x08;
channel4.envelope_sweep = data & 0x07;
return;
}
if(addr == 0xff22) { //NR43
channel4.shift_clock_frequency = (data >> 4) & 15;
channel4.counter_step_width = data & 0x08;
channel4.dividing_ratio = data & 0x07;
return;
}
if(addr == 0xff23) { //NR44
channel4.initialize = data & 0x80;
channel4.consecutive_selection = data & 0x40;
return;
}
if(addr == 0xff24) { //NR50
control.output_vin_to_so2 = data & 0x80;
control.so2_output_level = (data >> 4) & 7;
control.output_vin_to_so1 = data & 0x08;
control.so1_output_level = (data >> 0) & 7;
return;
}
if(addr == 0xff25) { //NR51
control.output_channel4_to_so2 = data & 0x80;
control.output_channel3_to_so2 = data & 0x40;
control.output_channel2_to_so2 = data & 0x20;
control.output_channel1_to_so2 = data & 0x10;
control.output_channel4_to_so1 = data & 0x08;
control.output_channel3_to_so1 = data & 0x04;
control.output_channel2_to_so1 = data & 0x02;
control.output_channel1_to_so1 = data & 0x01;
return;
}
if(addr == 0xff26) { //NR52
control.sound_on = data & 0x80;
control.channel4_on = data & 0x08;
control.channel3_on = data & 0x04;
control.channel2_on = data & 0x02;
control.channel1_on = data & 0x01;
return;
}
if(addr >= 0xff30 && addr <= 0xff3f) {
channel3.pattern[addr & 15] = data;
return;
}
}
#endif

View File

@@ -1,102 +0,0 @@
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
struct Channel1 { //tone and sweep
//$ff10 NR10
unsigned sweep_time;
bool sweep_direction;
unsigned sweep_shift;
//$ff11 NR11
unsigned wave_pattern_duty;
unsigned sound_length;
//$ff12 NR12
unsigned initial_envelope_volume;
bool envelope_direction;
unsigned envelope_sweep;
//$ff13,$ff14 NR13,NR14
unsigned frequency;
bool initialize;
bool consecutive_selection;
} channel1;
struct Channel2 { //tone
//$ff16 NR21
unsigned wave_pattern_duty;
unsigned sound_length;
//$ff17 NR22
unsigned initial_envelope_volume;
bool envelope_direction;
unsigned envelope_sweep;
//$ff18,$ff19 NR23,NR24
unsigned frequency;
bool initialize;
bool consecutive_selection;
} channel2;
struct Channel3 { //wave output
//$ff1a NR30
bool off;
//$ff1b NR31
unsigned sound_length;
//$ff1c NR32
unsigned output_level;
//$ff1d,$ff1e NR33,NR34
unsigned frequency;
bool initialize;
bool consecutive_selection;
//$ff30-ff3f
uint8 pattern[16];
} channel3;
struct Channel4 { //noise
//$ff20 NR41
unsigned sound_length;
//$ff21 NR42
unsigned initial_envelope_volume;
bool envelope_direction;
unsigned envelope_sweep;
//$ff22 NR43
unsigned shift_clock_frequency;
bool counter_step_width;
unsigned dividing_ratio;
//$ff23 NR44
bool initialize;
bool consecutive_selection;
} channel4;
struct Control {
//$ff24 NR50
bool output_vin_to_so2;
unsigned so2_output_level;
bool output_vin_to_so1;
unsigned so1_output_level;
//$ff25 NR51
bool output_channel4_to_so2;
bool output_channel3_to_so2;
bool output_channel2_to_so2;
bool output_channel1_to_so2;
bool output_channel4_to_so1;
bool output_channel3_to_so1;
bool output_channel2_to_so1;
bool output_channel1_to_so1;
//$ff26 NR52
bool sound_on;
bool channel4_on;
bool channel3_on;
bool channel2_on;
bool channel1_on;
} control;

102
bsnes/gameboy/apu/noise/noise.cpp Executable file
View File

@@ -0,0 +1,102 @@
#ifdef APU_CPP
void APU::Noise::run() {
if(period && --period == 0) {
period = divisor << frequency;
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << 14);
if(narrow_lfsr) lfsr |= (bit << 6);
}
}
uint4 sample = (lfsr & 1) ? 0 : volume;
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Noise::clock_length() {
if(counter && length) length--;
}
void APU::Noise::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) {
initial_length = 64 - (data & 0x3f);
length = initial_length;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
if(initialize) {
lfsr = ~0U;
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
}
}
}
void APU::Noise::power() {
initial_length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
narrow_lfsr = 0;
divisor = 0;
counter = 0;
output = 0;
length = 0;
envelope_period = 0;
volume = 0;
period = 0;
lfsr = 0;
}
void APU::Noise::serialize(serializer &s) {
s.integer(initial_length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(narrow_lfsr);
s.integer(divisor);
s.integer(counter);
s.integer(output);
s.integer(length);
s.integer(envelope_period);
s.integer(volume);
s.integer(period);
s.integer(lfsr);
}
#endif

View File

@@ -0,0 +1,24 @@
struct Noise {
unsigned initial_length;
unsigned envelope_volume;
bool envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
bool narrow_lfsr;
unsigned divisor;
bool counter;
int16 output;
unsigned length;
unsigned envelope_period;
unsigned volume;
unsigned period;
uint15 lfsr;
void run();
void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -1,6 +1,15 @@
#ifdef APU_CPP
void APU::serialize(serializer &s) {
s.array(mmio_data);
s.integer(sequencer_base);
s.integer(sequencer_step);
square1.serialize(s);
square2.serialize(s);
wave.serialize(s);
noise.serialize(s);
master.serialize(s);
}
#endif

View File

@@ -0,0 +1,146 @@
#ifdef APU_CPP
void APU::Square1::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Square1::sweep() {
if(enable == false) return;
signed offset = frequency_shadow >> sweep_shift;
if(sweep_direction) offset = -offset;
frequency_shadow += offset;
if(frequency_shadow < 0) {
frequency_shadow = 0;
} else if(frequency_shadow > 2047) {
frequency_shadow = 2048;
enable = false;
}
if(frequency_shadow <= 2047 && sweep_shift) {
frequency = frequency_shadow;
period = 4 * (2048 - frequency);
}
}
void APU::Square1::clock_length() {
if(counter && length) length--;
}
void APU::Square1::clock_sweep() {
if(sweep_frequency && sweep_period && --sweep_period == 0) {
sweep_period = sweep_frequency;
sweep();
}
}
void APU::Square1::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Square1::write(unsigned r, uint8 data) {
if(r == 0) {
sweep_frequency = (data >> 4) & 7;
sweep_direction = data & 0x08;
sweep_shift = data & 0x07;
}
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
envelope_period = envelope_frequency;
volume = envelope_volume;
frequency_shadow = frequency;
sweep_period = sweep_frequency;
enable = sweep_period || sweep_shift;
if(sweep_shift) sweep();
}
}
period = 4 * (2048 - frequency);
}
void APU::Square1::power() {
sweep_frequency = 0;
sweep_direction = 0;
sweep_shift = 0;
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
counter = 0;
output = 0;
duty_output = 0;
phase = 0;
period = 0;
envelope_period = 0;
sweep_period = 0;
frequency_shadow = 0;
enable = 0;
volume = 0;
}
void APU::Square1::serialize(serializer &s) {
s.integer(sweep_frequency);
s.integer(sweep_direction);
s.integer(sweep_shift);
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
s.integer(duty_output);
s.integer(phase);
s.integer(period);
s.integer(envelope_period);
s.integer(sweep_period);
s.integer(frequency_shadow);
s.integer(enable);
s.integer(volume);
}
#endif

View File

@@ -0,0 +1,31 @@
struct Square1 {
unsigned sweep_frequency;
unsigned sweep_direction;
unsigned sweep_shift;
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned sweep_period;
signed frequency_shadow;
bool enable;
unsigned volume;
void run();
void sweep();
void clock_length();
void clock_sweep();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -0,0 +1,97 @@
#ifdef APU_CPP
void APU::Square2::run() {
if(period && --period == 0) {
period = 4 * (2048 - frequency);
phase = (phase + 1) & 7;
switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__
}
}
uint4 sample = (duty_output ? volume : 0);
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
void APU::Square2::clock_length() {
if(counter && length) length--;
}
void APU::Square2::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
}
void APU::Square2::write(unsigned r, uint8 data) {
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
envelope_period = envelope_frequency;
volume = envelope_volume;
}
}
period = 4 * (2048 - frequency);
}
void APU::Square2::power() {
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_frequency = 0;
frequency = 0;
counter = 0;
output = 0;
duty_output = 0;
phase = 0;
period = 0;
envelope_period = 0;
volume = 0;
}
void APU::Square2::serialize(serializer &s) {
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
s.integer(duty_output);
s.integer(phase);
s.integer(period);
s.integer(envelope_period);
s.integer(volume);
}
#endif

View File

@@ -0,0 +1,23 @@
struct Square2 {
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned volume;
void run();
void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

103
bsnes/gameboy/apu/wave/wave.cpp Executable file
View File

@@ -0,0 +1,103 @@
#ifdef APU_CPP
void APU::Wave::run() {
if(period && --period == 0) {
period = 2 * (2048 - frequency);
pattern_offset = (pattern_offset + 1) & 31;
pattern_sample = pattern[pattern_offset];
}
uint4 sample = pattern_sample;
if(counter && length == 0) sample = 0;
if(enable == false) sample = 0;
output = (sample * 4369) - 32768;
output >>= volume;
}
void APU::Wave::clock_length() {
if(counter && length) length--;
}
void APU::Wave::write(unsigned r, uint8 data) {
if(r == 0) {
dac_enable = data & 0x80;
if(dac_enable == false) enable = false;
}
if(r == 1) {
initial_length = 256 - data;
length = initial_length;
}
if(r == 2) {
switch((data >> 5) & 3) {
case 0: volume = 16; break; // 0%
case 1: volume = 0; break; //100%
case 2: volume = 1; break; // 50%
case 3: volume = 2; break; // 25%
}
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize && dac_enable) {
enable = true;
pattern_offset = 0;
length = initial_length;
}
}
period = 2 * (2048 - frequency);
}
void APU::Wave::write_pattern(unsigned p, uint8 data) {
p <<= 1;
pattern[p + 0] = (data >> 4) & 15;
pattern[p + 1] = (data >> 0) & 15;
}
void APU::Wave::power() {
dac_enable = 0;
initial_length = 0;
volume = 0;
frequency = 0;
counter = 0;
random_cyclic r;
foreach(n, pattern) n = r() & 15;
output = 0;
enable = 0;
length = 0;
period = 0;
pattern_offset = 0;
pattern_sample = 0;
}
void APU::Wave::serialize(serializer &s) {
s.integer(dac_enable);
s.integer(initial_length);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
s.array(pattern);
s.integer(output);
s.integer(enable);
s.integer(length);
s.integer(period);
s.integer(pattern_offset);
s.integer(pattern_sample);
}
#endif

22
bsnes/gameboy/apu/wave/wave.hpp Executable file
View File

@@ -0,0 +1,22 @@
struct Wave {
bool dac_enable;
unsigned initial_length;
unsigned volume;
unsigned frequency;
bool counter;
uint8 pattern[32];
int16 output;
bool enable;
unsigned length;
unsigned period;
unsigned pattern_offset;
unsigned pattern_sample;
void run();
void clock_length();
void write(unsigned r, uint8 data);
void write_pattern(unsigned p, uint8 data);
void power();
void serialize(serializer&);
};

View File

@@ -70,7 +70,19 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
}
}
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
}
ramdata = new uint8_t[ramsize = info.ramsize]();
system.load();
loaded = true;
}
@@ -104,7 +116,19 @@ void Cartridge::ram_write(unsigned addr, uint8 data) {
ramdata[addr] = data;
}
uint8 Cartridge::mmio_read(uint16 addr) {
if(bootrom_enable && within<0x0000, 0x00ff>(addr)) return System::BootROM::sgb[addr];
return mapper->mmio_read(addr);
}
void Cartridge::mmio_write(uint16 addr, uint8 data) {
if(bootrom_enable && addr == 0xff50) bootrom_enable = false;
mapper->mmio_write(addr, data);
}
void Cartridge::power() {
bootrom_enable = true;
mbc0.power();
mbc1.power();
mbc2.power();
@@ -113,26 +137,10 @@ void Cartridge::power() {
mmm01.power();
huc1.power();
huc3.power();
map();
}
void Cartridge::map() {
MMIO *mapper = 0;
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
}
if(mapper) {
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = mapper;
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = mapper;
}
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = this;
}
Cartridge::Cartridge() {

View File

@@ -1,4 +1,4 @@
struct Cartridge : property<Cartridge> {
struct Cartridge : MMIO, property<Cartridge> {
#include "mbc0/mbc0.hpp"
#include "mbc1/mbc1.hpp"
#include "mbc2/mbc2.hpp"
@@ -41,6 +41,9 @@ struct Cartridge : property<Cartridge> {
uint8_t *ramdata;
unsigned ramsize;
MMIO *mapper;
bool bootrom_enable;
void load(const string &xml, const uint8_t *data, unsigned size);
void unload();
@@ -49,8 +52,10 @@ struct Cartridge : property<Cartridge> {
uint8 ram_read(unsigned addr);
void ram_write(unsigned addr, uint8 data);
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
void map();
void serialize(serializer&);
Cartridge();

View File

@@ -2,6 +2,7 @@
void Cartridge::serialize(serializer &s) {
if(info.battery) s.array(ramdata, ramsize);
s.integer(bootrom_enable);
s.integer(mbc1.ram_enable);
s.integer(mbc1.rom_select);

View File

@@ -94,7 +94,7 @@ void CPU::interrupt_exec(uint16 pc) {
}
void CPU::power() {
create(Main, 4 * 1024 * 1024);
create(Main, 4194304);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
@@ -114,13 +114,7 @@ void CPU::power() {
status.clock = 0;
status.halt = false;
status.stop = false;
status.ime = 0;
status.timer0 = 0;
status.timer1 = 0;
status.timer2 = 0;
status.timer3 = 0;
status.timer4 = 0;
status.p15 = 0;
status.p14 = 0;

View File

@@ -17,13 +17,7 @@ struct CPU : Processor, MMIO {
unsigned clock;
bool halt;
bool stop;
bool ime;
unsigned timer0;
unsigned timer1;
unsigned timer2;
unsigned timer3;
unsigned timer4;
//$ff00 JOYP
bool p15;

View File

@@ -21,13 +21,7 @@ void CPU::serialize(serializer &s) {
s.integer(status.clock);
s.integer(status.halt);
s.integer(status.stop);
s.integer(status.ime);
s.integer(status.timer0);
s.integer(status.timer1);
s.integer(status.timer2);
s.integer(status.timer3);
s.integer(status.timer4);
s.integer(status.p15);
s.integer(status.p14);

View File

@@ -1,13 +1,9 @@
//4194304hz (4 * 1024 * 1024)
//70224 clocks/frame
// 456 clocks/scanline
// 154 scanlines/frame
//4194304 / 4096 = 1024
//4194304 / 262144 = 16
//4194304 / 65536 = 64
//4394304 / 16384 = 256
#ifdef CPU_CPP
#include "opcode.cpp"
@@ -17,43 +13,44 @@ void CPU::add_clocks(unsigned clocks) {
scheduler.exit(Scheduler::ExitReason::StepEvent);
status.clock += clocks;
if(status.clock >= 4 * 1024 * 1024) {
status.clock -= 4 * 1024 * 1024;
if(status.clock >= 4194304) {
status.clock -= 4194304;
cartridge.mbc3.second();
}
status.timer0 += clocks;
if(status.timer0 >= 16) timer_stage0();
//4194304 / N(hz) - 1 = mask
if((status.clock & 15) == 0) timer_262144hz();
if((status.clock & 63) == 0) timer_65536hz();
if((status.clock & 255) == 0) timer_16384hz();
if((status.clock & 511) == 0) timer_8192hz();
if((status.clock & 1023) == 0) timer_4096hz();
cpu.clock += clocks;
if(cpu.clock >= 0) co_switch(scheduler.active_thread = lcd.thread);
lcd.clock -= clocks;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
apu.clock -= clocks;
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
}
void CPU::timer_stage0() { //262144hz
void CPU::timer_262144hz() {
if(status.timer_enable && status.timer_clock == 1) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer0 -= 16;
if(++status.timer1 >= 4) timer_stage1();
}
void CPU::timer_stage1() { // 65536hz
void CPU::timer_65536hz() {
if(status.timer_enable && status.timer_clock == 2) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer1 -= 4;
if(++status.timer2 >= 4) timer_stage2();
}
void CPU::timer_stage2() { // 16384hz
void CPU::timer_16384hz() {
if(status.timer_enable && status.timer_clock == 3) {
if(++status.tima == 0) {
status.tima = status.tma;
@@ -62,32 +59,24 @@ void CPU::timer_stage2() { // 16384hz
}
status.div++;
status.timer2 -= 4;
if(++status.timer3 >= 2) timer_stage3();
}
void CPU::timer_stage3() { // 8192hz
void CPU::timer_8192hz() {
if(status.serial_transfer && status.serial_clock) {
if(--status.serial_bits == 0) {
status.serial_transfer = 0;
interrupt_raise(Interrupt::Serial);
}
}
status.timer3 -= 2;
if(++status.timer4 >= 2) timer_stage4();
}
void CPU::timer_stage4() { // 4096hz
void CPU::timer_4096hz() {
if(status.timer_enable && status.timer_clock == 0) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer4 -= 2;
}
#endif

View File

@@ -1,9 +1,9 @@
void add_clocks(unsigned clocks);
void timer_stage0();
void timer_stage1();
void timer_stage2();
void timer_stage3();
void timer_stage4();
void timer_262144hz();
void timer_65536hz();
void timer_16384hz();
void timer_8192hz();
void timer_4096hz();
//opcode.cpp
void op_io();

View File

@@ -5,7 +5,7 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000.13";
static const char Version[] = "000.19";
static unsigned SerializerVersion = 1;
}
}
@@ -15,22 +15,56 @@ namespace GameBoy {
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/varint.hpp>
using namespace nall;
namespace GameBoy {
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef uint_t< 1> uint1;
typedef uint_t< 2> uint2;
typedef uint_t< 3> uint3;
typedef uint_t< 4> uint4;
typedef uint_t< 5> uint5;
typedef uint_t< 6> uint6;
typedef uint_t< 7> uint7;
typedef uint_t< 9> uint9;
typedef uint_t<10> uint10;
typedef uint_t<11> uint11;
typedef uint_t<12> uint12;
typedef uint_t<13> uint13;
typedef uint_t<14> uint14;
typedef uint_t<15> uint15;
typedef uint_t<17> uint17;
typedef uint_t<18> uint18;
typedef uint_t<19> uint19;
typedef uint_t<20> uint20;
typedef uint_t<21> uint21;
typedef uint_t<22> uint22;
typedef uint_t<23> uint23;
typedef uint_t<24> uint24;
typedef uint_t<25> uint25;
typedef uint_t<26> uint26;
typedef uint_t<27> uint27;
typedef uint_t<28> uint28;
typedef uint_t<29> uint29;
typedef uint_t<30> uint30;
typedef uint_t<31> uint31;
template<uint16 lo, uint16 hi>
alwaysinline bool within(uint16 addr) {
static const uint16 mask = ~(hi ^ lo);

View File

@@ -3,7 +3,7 @@ public:
virtual void joyp_write(bool p15, bool p14) {}
virtual void video_refresh(const uint8_t *data) {}
virtual void audio_sample(signed left, signed right) {}
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
virtual void input_poll() {}
virtual bool input_poll(unsigned id) {}

View File

@@ -33,8 +33,8 @@ void LCD::add_clocks(unsigned clocks) {
status.lx += clocks;
if(status.lx >= 456) scanline();
cpu.clock -= clocks;
if(cpu.clock <= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}
}
@@ -190,7 +190,7 @@ void LCD::render_obj() {
}
void LCD::power() {
create(Main, 4 * 1024 * 1024);
create(Main, 4194304);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO

View File

@@ -50,7 +50,7 @@ void Bus::write(uint16 addr, uint8 data) {
}
void Bus::power() {
for(unsigned n = 0; n < 65536; n++) mmio[n] = &unmapped;
for(unsigned n = 0x0000; n <= 0xffff; n++) mmio[n] = &unmapped;
}
}

View File

@@ -33,23 +33,14 @@ void System::runthreadtosave() {
}
}
uint8 System::mmio_read(uint16 addr) {
if((addr & 0xff00) == 0x0000) {
return BootROM::sgb[addr];
}
return 0x00;
}
void System::mmio_write(uint16 addr, uint8 data) {
if(addr == 0xff50) {
if(data == 0x01) cartridge.map();
}
}
void System::init(Interface *interface_) {
interface = interface_;
}
void System::load() {
serialize_init();
}
void System::power() {
bus.power();
cartridge.power();
@@ -58,11 +49,7 @@ void System::power() {
lcd.power();
scheduler.init();
for(unsigned n = 0x0000; n <= 0x00ff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = this;
clocks_executed = 0;
serialize_init();
}
}

View File

@@ -4,7 +4,7 @@ enum class Input : unsigned {
Up, Down, Left, Right, B, A, Select, Start,
};
struct System : MMIO {
struct System {
struct BootROM {
static const uint8 dmg[256];
static const uint8 sgb[256];
@@ -14,10 +14,8 @@ struct System : MMIO {
void runtosave();
void runthreadtosave();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void init(Interface*);
void load();
void power();
Interface *interface;

View File

@@ -5,8 +5,14 @@
#include <nall/concept.hpp>
#undef foreach
#define foreach(iter, object) \
#define foreach2(iter, object) foreach3(iter, object, foreach_counter)
#define foreach3(iter, object, foreach_counter) \
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
#define foreach_impl(...) foreach_decl(__VA_ARGS__, foreach3(__VA_ARGS__), foreach2(__VA_ARGS__), foreach_too_few_arguments)
#define foreach_decl(_1, _2, _3, N, ...) N
#define foreach(...) foreach_impl(__VA_ARGS__)
#endif

32
bsnes/nall/public_cast.hpp Executable file
View File

@@ -0,0 +1,32 @@
#ifndef NALL_PUBLIC_CAST_HPP
#define NALL_PUBLIC_CAST_HPP
//this is a proof-of-concept-*only* C++ access-privilege elevation exploit.
//this code is 100% legal C++, per C++98 section 14.7.2 paragraph 8:
//"access checking rules do not apply to names in explicit instantiations."
//usage example:
//struct N { typedef void (Class::*)(); };
//template class public_cast<N, &Class::Reference>;
//(class.*public_cast<N>::value);
//Class::Reference may be public, protected or private
//Class::Reference may be a function, object or variable
namespace nall {
template<typename T, typename T::type... P> struct public_cast;
template<typename T> struct public_cast<T> {
static typename T::type value;
};
template<typename T> typename T::type public_cast<T>::value;
template<typename T, typename T::type P> struct public_cast<T, P> {
static typename T::type value;
};
template<typename T, typename T::type P> typename T::type public_cast<T, P>::value = public_cast<T>::value = P;
}
#endif

103
bsnes/nall/reference_array.hpp Executable file
View File

@@ -0,0 +1,103 @@
#ifndef NALL_REFERENCE_ARRAY_HPP
#define NALL_REFERENCE_ARRAY_HPP
#include <type_traits>
#include <nall/bit.hpp>
#include <nall/concept.hpp>
namespace nall {
template<typename T> struct reference_array {
protected:
typedef typename std::remove_reference<T>::type *Tptr;
Tptr *pool;
unsigned poolsize, buffersize;
public:
unsigned size() const { return buffersize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = 0;
poolsize = 0;
buffersize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (Tptr*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
buffersize = min(buffersize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize));
buffersize = newsize;
}
void append(const T data) {
unsigned index = buffersize++;
if(index >= poolsize) resize(index + 1);
pool[index] = &data;
}
template<typename... Args> reference_array(Args&... args) : pool(0), poolsize(0), buffersize(0) {
construct(args...);
}
~reference_array() {
reset();
}
reference_array& operator=(const reference_array &source) {
if(pool) free(pool);
buffersize = source.buffersize;
poolsize = source.poolsize;
pool = (Tptr*)malloc(sizeof(T) * poolsize);
memcpy(pool, source.pool, sizeof(T) * buffersize);
return *this;
}
reference_array& operator=(const reference_array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
buffersize = source.buffersize;
source.pool = 0;
source.reset();
return *this;
}
inline T operator[](unsigned index) {
if(index >= buffersize) throw "reference_array[] out of bounds";
return *pool[index];
}
inline const T operator[](unsigned index) const {
if(index >= buffersize) throw "reference_array[] out of bounds";
return *pool[index];
}
private:
void construct() {
}
void construct(const reference_array &source) {
operator=(source);
}
void construct(const reference_array &&source) {
operator=(std::move(source));
}
template<typename... Args> void construct(T data, Args&... args) {
append(data);
construct(args...);
}
};
template<typename T> struct has_size<reference_array<T>> { enum { value = true }; };
}
#endif

View File

@@ -25,7 +25,7 @@ namespace nall {
inline string& append(unsigned int value);
inline string& append(double value);
inline bool readfile(const char*);
inline bool readfile(const string&);
inline string& replace (const char*, const char*);
inline string& qreplace(const char*, const char*);

View File

@@ -65,8 +65,11 @@ intmax_t integer(const char *str) {
intmax_t result = 0;
bool negate = false;
//check for negation
if(*str == '-') {
//check for sign
if(*str == '+') {
negate = false;
str++;
} else if(*str == '-') {
negate = true;
str++;
}

View File

@@ -95,7 +95,7 @@ string::~string() {
if(data) free(data);
}
bool string::readfile(const char *filename) {
bool string::readfile(const string &filename) {
assign("");
#if !defined(_WIN32)

View File

@@ -50,10 +50,10 @@ string integer(intmax_t value) {
result[x] = buffer[y];
}
return result;
return (const char*)result;
}
template<unsigned length> string linteger(intmax_t value) {
template<unsigned length_> string linteger(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
@@ -68,6 +68,7 @@ template<unsigned length> string linteger(intmax_t value) {
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
@@ -76,10 +77,10 @@ template<unsigned length> string linteger(intmax_t value) {
result[x] = buffer[y];
}
return result;
return (const char*)result;
}
template<unsigned length> string rinteger(intmax_t value) {
template<unsigned length_> string rinteger(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
@@ -94,6 +95,7 @@ template<unsigned length> string rinteger(intmax_t value) {
buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
@@ -102,7 +104,7 @@ template<unsigned length> string rinteger(intmax_t value) {
result[x] = buffer[y];
}
return result;
return (const char*)result;
}
string decimal(uintmax_t value) {
@@ -124,10 +126,10 @@ string decimal(uintmax_t value) {
result[x] = buffer[y];
}
return result;
return (const char*)result;
}
template<unsigned length> string ldecimal(uintmax_t value) {
template<unsigned length_> string ldecimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
@@ -138,6 +140,7 @@ template<unsigned length> string ldecimal(uintmax_t value) {
} while(value);
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
@@ -146,10 +149,10 @@ template<unsigned length> string ldecimal(uintmax_t value) {
result[x] = buffer[y];
}
return result;
return (const char*)result;
}
template<unsigned length> string rdecimal(uintmax_t value) {
template<unsigned length_> string rdecimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
@@ -160,6 +163,7 @@ template<unsigned length> string rdecimal(uintmax_t value) {
} while(value);
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, ' ', length);
result[length] = 0;
@@ -168,53 +172,51 @@ template<unsigned length> string rdecimal(uintmax_t value) {
result[x] = buffer[y];
}
return result;
return (const char*)result;
}
template<unsigned length> string hex(uintmax_t value) {
string output;
unsigned offset = 0;
template<unsigned length_> string hex(uintmax_t value) {
char buffer[64];
unsigned size = 0;
//render string backwards, as we do not know its length yet
do {
unsigned n = value & 15;
output[offset++] = n < 10 ? '0' + n : 'a' + n - 10;
buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
while(offset < length) output[offset++] = '0';
output[offset--] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
result[length] = 0;
//reverse the string in-place
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return output;
return (const char*)result;
}
template<unsigned length> string binary(uintmax_t value) {
string output;
unsigned offset = 0;
template<unsigned length_> string binary(uintmax_t value) {
char buffer[256];
unsigned size = 0;
do {
unsigned n = value & 1;
output[offset++] = '0' + n;
buffer[size++] = '0' + n;
value >>= 1;
} while(value);
while(offset < length) output[offset++] = '0';
output[offset--] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
memset(result, '0', length);
result[length] = 0;
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
char temp = output[i];
output[i] = output[offset - i];
output[offset - i] = temp;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
result[x] = buffer[y];
}
return output;
return (const char*)result;
}
//using sprintf is certainly not the most ideal method to convert

170
bsnes/phoenix/core/core.cpp Executable file
View File

@@ -0,0 +1,170 @@
#include "state.hpp"
#include "layout/fixed-layout.cpp"
#include "layout/horizontal-layout.cpp"
#include "layout/vertical-layout.cpp"
#if defined(PHOENIX_WINDOWS)
#include "../windows/windows.cpp"
#elif defined(PHOENIX_QT)
#include "../qt/qt.cpp"
#elif defined(PHOENIX_GTK)
#include "../gtk/gtk.cpp"
#elif defined(PHOENIX_REFERENCE)
#include "../reference/reference.cpp"
#endif
Object::Object() { OS::initialize(); }
Geometry OS::availableGeometry() { return pOS::availableGeometry(); }
Geometry OS::desktopGeometry() { return pOS::desktopGeometry(); }
string OS::fileLoad_(Window &parent, const string &path, const lstring &filter_) { auto filter = filter_; if(filter.size() == 0) filter.append("All files (*)"); return pOS::fileLoad(parent, path, filter); }
string OS::fileSave_(Window &parent, const string &path, const lstring &filter_) { auto filter = filter_; if(filter.size() == 0) filter.append("All files (*)"); return pOS::fileSave(parent, path, filter); }
string OS::folderSelect(Window &parent, const string &path) { return pOS::folderSelect(parent, path); }
void OS::main() { return pOS::main(); }
bool OS::pendingEvents() { return pOS::pendingEvents(); }
void OS::processEvents() { return pOS::processEvents(); }
void OS::quit() { return pOS::quit(); }
void OS::initialize() { static bool initialized = false; if(initialized == false) { initialized = true; return pOS::initialize(); } }
void Font::setBold(bool bold) { state.bold = bold; return p.setBold(bold); }
void Font::setFamily(const string &family) { state.family = family; return p.setFamily(family); }
void Font::setItalic(bool italic) { state.italic = italic; return p.setItalic(italic); }
void Font::setSize(unsigned size) { state.size = size; return p.setSize(size); }
void Font::setUnderline(bool underline) { state.underline = underline; return p.setUnderline(underline); }
Font::Font() : state(*new State), p(*new pFont(*this)) { p.constructor(); }
MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::information(parent, text, buttons); }
MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::question(parent, text, buttons); }
MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::warning(parent, text, buttons); }
MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::critical(parent, text, buttons); }
Window Window::None;
void Window::append(Layout &layout) { state.layout.append(layout); return p.append(layout); }
void Window::append(Menu &menu) { state.menu.append(menu); ((Action&)menu).state.parent = this; return p.append(menu); }
void Window::append(Widget &widget) { state.widget.append(widget); return p.append(widget); }
Geometry Window::frameGeometry() { Geometry geometry = p.geometry(), margin = p.frameMargin(); return { geometry.x - margin.x, geometry.y - margin.y, geometry.width + margin.width, geometry.height + margin.height }; }
Geometry Window::frameMargin() { return p.frameMargin(); }
bool Window::focused() { return p.focused(); }
Geometry Window::geometry() { return p.geometry(); }
void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { state.backgroundColor = true; state.backgroundColorRed = red; state.backgroundColorGreen = green; state.backgroundColorBlue = blue; return p.setBackgroundColor(red, green, blue); }
void Window::setFrameGeometry(const Geometry &geometry) { Geometry margin = p.frameMargin(); return setGeometry({ geometry.x + margin.x, geometry.y + margin.y, geometry.width - margin.width, geometry.height - margin.height }); }
void Window::setFocused() { return p.setFocused(); }
void Window::setFullScreen(bool fullScreen) { state.fullScreen = fullScreen; return p.setFullScreen(fullScreen); }
void Window::setGeometry(const Geometry &geometry) { state.geometry = geometry; return p.setGeometry(geometry); }
void Window::setMenuFont(Font &font) { state.menuFont = &font; return p.setMenuFont(font); }
void Window::setMenuVisible(bool visible) { state.menuVisible = visible; return p.setMenuVisible(visible); }
void Window::setResizable(bool resizable) { state.resizable = resizable; return p.setResizable(resizable); }
void Window::setStatusFont(Font &font) { state.statusFont = &font; return p.setStatusFont(font); }
void Window::setStatusText(const string &text) { state.statusText = text; return p.setStatusText(text); }
void Window::setStatusVisible(bool visible) { state.statusVisible = visible; return p.setStatusVisible(visible); }
void Window::setTitle(const string &text) { state.title = text; return p.setTitle(text); }
void Window::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
void Window::setWidgetFont(Font &font) { state.widgetFont = &font; return p.setWidgetFont(font); }
Window::Window() : state(*new State), p(*new pWindow(*this)) { p.constructor(); }
void Action::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Action::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
Action::Action(pAction &p) : state(*new State), p(p) { p.constructor(); }
void Menu::append(Action &action) { state.action.append(action); return p.append(action); }
void Menu::setText(const string &text) { state.text = text; return p.setText(text); }
Menu::Menu() : state(*new State), base_from_member<pMenu&>(*new pMenu(*this)), Action(base_from_member<pMenu&>::value), p(base_from_member<pMenu&>::value) { p.constructor(); }
Separator::Separator() : base_from_member<pSeparator&>(*new pSeparator(*this)), Action(base_from_member<pSeparator&>::value), p(base_from_member<pSeparator&>::value) { p.constructor(); }
void Item::setText(const string &text) { state.text = text; return p.setText(text); }
Item::Item() : state(*new State), base_from_member<pItem&>(*new pItem(*this)), Action(base_from_member<pItem&>::value), p(base_from_member<pItem&>::value) { p.constructor(); }
bool CheckItem::checked() { return p.checked(); }
void CheckItem::setChecked(bool checked) { state.checked = checked; return p.setChecked(checked); }
void CheckItem::setText(const string &text) { state.text = text; return p.setText(text); }
CheckItem::CheckItem() : state(*new State), base_from_member<pCheckItem&>(*new pCheckItem(*this)), Action(base_from_member<pCheckItem&>::value), p(base_from_member<pCheckItem&>::value) { p.constructor(); }
void RadioItem::group_(const reference_array<RadioItem&> &list) { foreach(item, list) item.p.setGroup(item.state.group = list); if(list.size()) list[0].setChecked(); }
bool RadioItem::checked() { return p.checked(); }
void RadioItem::setChecked() { foreach(item, state.group) item.state.checked = false; state.checked = true; return p.setChecked(); }
void RadioItem::setText(const string &text) { state.text = text; return p.setText(text); }
RadioItem::RadioItem() : state(*new State), base_from_member<pRadioItem&>(*new pRadioItem(*this)), Action(base_from_member<pRadioItem&>::value), p(base_from_member<pRadioItem&>::value) { p.constructor(); }
bool Widget::enabled() { return state.enabled; }
void Widget::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Widget::setFocused() { return p.setFocused(); }
void Widget::setFont(Font &font) { state.font = &font; return p.setFont(font); }
void Widget::setGeometry(const Geometry &geometry) { state.geometry = geometry; return p.setGeometry(geometry); }
void Widget::setVisible(bool visible) { state.visible = visible; return p.setVisible(visible); }
bool Widget::visible() { return state.visible; }
Widget::Widget() : state(*new State), p(*new pWidget(*this)) { state.abstract = true; p.constructor(); }
Widget::Widget(pWidget &p) : state(*new State), p(p) { p.constructor(); }
void Button::setText(const string &text) { state.text = text; return p.setText(text); }
Button::Button() : state(*new State), base_from_member<pButton&>(*new pButton(*this)), Widget(base_from_member<pButton&>::value), p(base_from_member<pButton&>::value) { p.constructor(); }
bool CheckBox::checked() { return p.checked(); }
void CheckBox::setChecked(bool checked) { state.checked = checked; return p.setChecked(checked); }
void CheckBox::setText(const string &text) { state.text = text; return p.setText(text); }
CheckBox::CheckBox() : state(*new State), base_from_member<pCheckBox&>(*new pCheckBox(*this)), Widget(base_from_member<pCheckBox&>::value), p(base_from_member<pCheckBox&>::value) { p.constructor(); }
void ComboBox::append(const string &text) { state.text.append(text); return p.append(text); }
void ComboBox::reset() { state.selection = 0; state.text.reset(); return p.reset(); }
unsigned ComboBox::selection() { return p.selection(); }
void ComboBox::setSelection(unsigned row) { state.selection = row; return p.setSelection(row); }
ComboBox::ComboBox() : state(*new State), base_from_member<pComboBox&>(*new pComboBox(*this)), Widget(base_from_member<pComboBox&>::value), p(base_from_member<pComboBox&>::value) { p.constructor(); }
void HexEdit::setColumns(unsigned columns) { state.columns = columns; return p.setColumns(columns); }
void HexEdit::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HexEdit::setOffset(unsigned offset) { state.offset = offset; return p.setOffset(offset); }
void HexEdit::setRows(unsigned rows) { state.rows = rows; return p.setRows(rows); }
void HexEdit::update() { return p.update(); }
HexEdit::HexEdit() : state(*new State), base_from_member<pHexEdit&>(*new pHexEdit(*this)), Widget(base_from_member<pHexEdit&>::value), p(base_from_member<pHexEdit&>::value) { p.constructor(); }
unsigned HorizontalSlider::position() { return p.position(); }
void HorizontalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HorizontalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
HorizontalSlider::HorizontalSlider() : state(*new State), base_from_member<pHorizontalSlider&>(*new pHorizontalSlider(*this)), Widget(base_from_member<pHorizontalSlider&>::value), p(base_from_member<pHorizontalSlider&>::value) { p.constructor(); }
void Label::setText(const string &text) { state.text = text; return p.setText(text); }
Label::Label() : state(*new State), base_from_member<pLabel&>(*new pLabel(*this)), Widget(base_from_member<pLabel&>::value), p(base_from_member<pLabel&>::value) { p.constructor(); }
void LineEdit::setEditable(bool editable) { state.editable = editable; return p.setEditable(editable); }
void LineEdit::setText(const string &text) { state.text = text; return p.setText(text); }
string LineEdit::text() { return p.text(); }
LineEdit::LineEdit() : state(*new State), base_from_member<pLineEdit&>(*new pLineEdit(*this)), Widget(base_from_member<pLineEdit&>::value), p(base_from_member<pLineEdit&>::value) { p.constructor(); }
void ListView::append_(const lstring &text) { state.checked.append(false); state.text.append(text); return p.append(text); }
void ListView::autoSizeColumns() { return p.autoSizeColumns(); }
bool ListView::checked(unsigned row) { return p.checked(row); }
void ListView::modify_(unsigned row, const lstring &text) { state.text[row] = text; return p.modify(row, text); }
void ListView::reset() { state.checked.reset(); state.text.reset(); return p.reset(); }
bool ListView::selected() { return p.selected(); }
unsigned ListView::selection() { return p.selection(); }
void ListView::setCheckable(bool checkable) { state.checkable = checkable; return p.setCheckable(checkable); }
void ListView::setChecked(unsigned row, bool checked) { state.checked[row] = checked; return p.setChecked(row, checked); }
void ListView::setHeaderText_(const lstring &text) { state.headerText = text; return p.setHeaderText(text); }
void ListView::setHeaderVisible(bool visible) { state.headerVisible = visible; return p.setHeaderVisible(visible); }
void ListView::setSelected(bool selected) { state.selected = selected; return p.setSelected(selected); }
void ListView::setSelection(unsigned row) { state.selected = true; state.selection = row; return p.setSelection(row); }
ListView::ListView() : state(*new State), base_from_member<pListView&>(*new pListView(*this)), Widget(base_from_member<pListView&>::value), p(base_from_member<pListView&>::value) { p.constructor(); }
void ProgressBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
ProgressBar::ProgressBar() : state(*new State), base_from_member<pProgressBar&>(*new pProgressBar(*this)), Widget(base_from_member<pProgressBar&>::value), p(base_from_member<pProgressBar&>::value) { p.constructor(); }
void RadioBox::group_(const reference_array<RadioBox&> &list) { foreach(item, list) item.p.setGroup(item.state.group = list); if(list.size()) list[0].setChecked(); }
bool RadioBox::checked() { return p.checked(); }
void RadioBox::setChecked() { foreach(item, state.group) item.state.checked = false; state.checked = true; return p.setChecked(); }
void RadioBox::setText(const string &text) { state.text = text; return p.setText(text); }
RadioBox::RadioBox() : state(*new State), base_from_member<pRadioBox&>(*new pRadioBox(*this)), Widget(base_from_member<pRadioBox&>::value), p(base_from_member<pRadioBox&>::value) { p.constructor(); }
void TextEdit::setCursorPosition(unsigned position) { state.cursorPosition = position; return p.setCursorPosition(position); }
void TextEdit::setEditable(bool editable) { state.editable = editable; return p.setEditable(editable); }
void TextEdit::setText(const string &text) { state.text = text; return p.setText(text); }
void TextEdit::setWordWrap(bool wordWrap) { state.wordWrap = wordWrap; return p.setWordWrap(wordWrap); }
string TextEdit::text() { return p.text(); }
TextEdit::TextEdit() : state(*new State), base_from_member<pTextEdit&>(*new pTextEdit(*this)), Widget(base_from_member<pTextEdit&>::value), p(base_from_member<pTextEdit&>::value) { p.constructor(); }
unsigned VerticalSlider::position() { return p.position(); }
void VerticalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void VerticalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
VerticalSlider::VerticalSlider() : state(*new State), base_from_member<pVerticalSlider&>(*new pVerticalSlider(*this)), Widget(base_from_member<pVerticalSlider&>::value), p(base_from_member<pVerticalSlider&>::value) { p.constructor(); }
uintptr_t Viewport::handle() { return p.handle(); }
Viewport::Viewport() : base_from_member<pViewport&>(*new pViewport(*this)), Widget(base_from_member<pViewport&>::value), p(base_from_member<pViewport&>::value) { p.constructor(); }

405
bsnes/phoenix/core/core.hpp Executable file
View File

@@ -0,0 +1,405 @@
struct Font;
struct Window;
struct Menu;
struct Layout;
struct Widget;
struct pOS;
struct pFont;
struct pWindow;
struct pAction;
struct pMenu;
struct pSeparator;
struct pItem;
struct pCheckItem;
struct pRadioItem;
struct pLayout;
struct pWidget;
struct pButton;
struct pCheckBox;
struct pComboBox;
struct pHexEdit;
struct pHorizontalSlider;
struct pLabel;
struct pLineEdit;
struct pListView;
struct pProgressBar;
struct pRadioBox;
struct pTextEdit;
struct pVerticalSlider;
struct pViewport;
struct Geometry {
signed x, y;
unsigned width, height;
inline Geometry() : x(0), y(0), width(0), height(0) {}
inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
};
struct Object {
Object();
Object& operator=(const Object&) = delete;
Object(const Object&) = delete;
virtual void unused() {} //allows dynamic_cast<> on Object
};
struct OS : Object {
static Geometry availableGeometry();
static Geometry desktopGeometry();
template<typename... Args> static nall::string fileLoad(Window &parent, const nall::string &path, const Args&... args) { return fileLoad_(parent, path, { args... }); }
template<typename... Args> static nall::string fileSave(Window &parent, const nall::string &path, const Args&... args) { return fileSave_(parent, path, { args... }); }
static nall::string folderSelect(Window &parent, const nall::string &path);
static void main();
static bool pendingEvents();
static void processEvents();
static void quit();
OS();
static void initialize();
private:
static nall::string fileLoad_(Window &parent, const nall::string &path, const nall::lstring& filter);
static nall::string fileSave_(Window &parent, const nall::string &path, const nall::lstring& filter);
};
struct Font : Object {
void setBold(bool bold = true);
void setFamily(const nall::string &family);
void setItalic(bool italic = true);
void setSize(unsigned size);
void setUnderline(bool underline = true);
Font();
struct State;
State &state;
pFont &p;
};
struct MessageWindow : Object {
enum class Buttons : unsigned {
Ok,
OkCancel,
YesNo,
};
enum class Response : unsigned {
Ok,
Cancel,
Yes,
No,
};
static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo);
static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
};
struct Window : Object {
static Window None;
nall::function<void ()> onClose;
nall::function<void ()> onMove;
nall::function<void ()> onSize;
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
Geometry frameGeometry();
Geometry frameMargin();
bool focused();
Geometry geometry();
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setFrameGeometry(const Geometry &geometry);
void setFocused();
void setFullScreen(bool fullScreen = true);
void setGeometry(const Geometry &geometry);
void setMenuFont(Font &font);
void setMenuVisible(bool visible = true);
void setResizable(bool resizable = true);
void setStatusFont(Font &font);
void setStatusText(const nall::string &text);
void setStatusVisible(bool visible = true);
void setTitle(const nall::string &text);
void setVisible(bool visible = true);
void setWidgetFont(Font &font);
Window();
struct State;
State &state;
pWindow &p;
};
struct Action : Object {
void setEnabled(bool enabled = true);
void setVisible(bool visible = true);
Action(pAction &p);
struct State;
State &state;
pAction &p;
};
struct Menu : private nall::base_from_member<pMenu&>, Action {
void append(Action &action);
void setText(const nall::string &text);
Menu();
struct State;
State &state;
pMenu &p;
};
struct Separator : private nall::base_from_member<pSeparator&>, Action {
Separator();
pSeparator &p;
};
struct Item : private nall::base_from_member<pItem&>, Action {
nall::function<void ()> onTick;
void setText(const nall::string &text);
Item();
struct State;
State &state;
pItem &p;
};
struct CheckItem : private nall::base_from_member<pCheckItem&>, Action {
nall::function<void ()> onTick;
bool checked();
void setChecked(bool checked = true);
void setText(const nall::string &text);
CheckItem();
struct State;
State &state;
pCheckItem &p;
};
struct RadioItem : private nall::base_from_member<pRadioItem&>, Action {
template<typename... Args> static void group(Args&... args) { group_({ args... }); }
nall::function<void ()> onTick;
bool checked();
void setChecked();
void setText(const nall::string &text);
RadioItem();
struct State;
State &state;
pRadioItem &p;
private:
static void group_(const nall::reference_array<RadioItem&> &list);
};
struct Layout : Object {
virtual void setGeometry(Geometry &geometry) = 0;
virtual void setParent(Window &parent) = 0;
virtual void setVisible(bool visible = true) = 0;
};
struct Widget : Object {
bool enabled();
void setEnabled(bool enabled = true);
void setFocused();
void setFont(Font &font);
void setGeometry(const Geometry &geometry);
void setVisible(bool visible = true);
bool visible();
Widget();
Widget(pWidget &p);
struct State;
State &state;
pWidget &p;
};
struct Button : private nall::base_from_member<pButton&>, Widget {
nall::function<void ()> onTick;
void setText(const nall::string &text);
Button();
struct State;
State &state;
pButton &p;
};
struct CheckBox : private nall::base_from_member<pCheckBox&>, Widget {
nall::function<void ()> onTick;
bool checked();
void setChecked(bool checked = true);
void setText(const nall::string &text);
CheckBox();
struct State;
State &state;
pCheckBox &p;
};
struct ComboBox : private nall::base_from_member<pComboBox&>, Widget {
nall::function<void ()> onChange;
void append(const nall::string &text);
void reset();
unsigned selection();
void setSelection(unsigned row);
ComboBox();
struct State;
State &state;
pComboBox &p;
};
struct HexEdit : private nall::base_from_member<pHexEdit&>, Widget {
nall::function<uint8_t (unsigned)> onRead;
nall::function<void (unsigned, uint8_t)> onWrite;
void setColumns(unsigned columns);
void setLength(unsigned length);
void setOffset(unsigned offset);
void setRows(unsigned rows);
void update();
HexEdit();
struct State;
State &state;
pHexEdit &p;
};
struct HorizontalSlider : private nall::base_from_member<pHorizontalSlider&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
HorizontalSlider();
struct State;
State &state;
pHorizontalSlider &p;
};
struct Label : private nall::base_from_member<pLabel&>, Widget {
void setText(const nall::string &text);
Label();
struct State;
State &state;
pLabel &p;
};
struct LineEdit : private nall::base_from_member<pLineEdit&>, Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
void setEditable(bool editable = true);
void setText(const nall::string &text);
nall::string text();
LineEdit();
struct State;
State &state;
pLineEdit &p;
};
struct ListView : private nall::base_from_member<pListView&>, Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
nall::function<void (unsigned)> onTick;
template<typename... Args> void append(const Args&... args) { append_({ args... }); }
void autoSizeColumns();
bool checked(unsigned row);
template<typename... Args> void modify(unsigned row, const Args&... args) { modify_(row, { args... }); }
void reset();
bool selected();
unsigned selection();
void setCheckable(bool checkable = true);
void setChecked(unsigned row, bool checked = true);
template<typename... Args> void setHeaderText(const Args&... args) { setHeaderText_({ args... }); }
void setHeaderVisible(bool visible = true);
void setSelected(bool selected = true);
void setSelection(unsigned row);
ListView();
struct State;
State &state;
pListView &p;
private:
void append_(const nall::lstring &list);
void modify_(unsigned row, const nall::lstring &list);
void setHeaderText_(const nall::lstring &list);
};
struct ProgressBar : private nall::base_from_member<pProgressBar&>, Widget {
void setPosition(unsigned position);
ProgressBar();
struct State;
State &state;
pProgressBar &p;
};
struct RadioBox : private nall::base_from_member<pRadioBox&>, Widget {
template<typename... Args> static void group(Args&... args) { group_({ args... }); }
nall::function<void ()> onTick;
bool checked();
void setChecked();
void setText(const nall::string &text);
RadioBox();
struct State;
State &state;
pRadioBox &p;
private:
static void group_(const nall::reference_array<RadioBox&> &list);
};
struct TextEdit : private nall::base_from_member<pTextEdit&>, Widget {
nall::function<void ()> onChange;
void setCursorPosition(unsigned position);
void setEditable(bool editable = true);
void setText(const nall::string &text);
void setWordWrap(bool wordWrap = true);
nall::string text();
TextEdit();
struct State;
State &state;
pTextEdit &p;
};
struct VerticalSlider : private nall::base_from_member<pVerticalSlider&>, Widget {
nall::function<void ()> onChange;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
VerticalSlider();
struct State;
State &state;
pVerticalSlider &p;
};
struct Viewport : private nall::base_from_member<pViewport&>, Widget {
uintptr_t handle();
Viewport();
pViewport &p;
};
#include "layout/fixed-layout.hpp"
#include "layout/horizontal-layout.hpp"
#include "layout/vertical-layout.hpp"

View File

@@ -0,0 +1,20 @@
void FixedLayout::setParent(Window &parent) {
foreach(child, children) {
parent.append(*child.widget);
child.widget->setGeometry(child.geometry);
}
}
void FixedLayout::append(Widget &widget, const Geometry &geometry) {
children.append({ &widget, geometry });
}
void FixedLayout::setGeometry(Geometry &geometry) {
}
void FixedLayout::setVisible(bool visible) {
foreach(child, children) child.widget->setVisible(visible);
}
FixedLayout::FixedLayout() {
}

View File

@@ -0,0 +1,15 @@
struct FixedLayout : Layout {
void append(Widget &widget, const Geometry &geometry);
void setGeometry(Geometry &geometry);
void setParent(Window &parent);
void setVisible(bool visible);
FixedLayout();
//private:
Window *parent;
struct Children {
Widget *widget;
Geometry geometry;
};
nall::linear_vector<Children> children;
};

View File

@@ -0,0 +1,88 @@
void HorizontalLayout::setParent(Window &parent) {
foreach(child, children) {
if(child.layout) child.layout->setParent(parent);
if(child.widget) parent.append(*child.widget);
}
}
void HorizontalLayout::append(VerticalLayout &layout, unsigned width, unsigned height, unsigned spacing) {
layout.width = width;
layout.height = height;
children.append({ &layout, 0, width, height, spacing });
}
void HorizontalLayout::append(Widget &widget, unsigned width, unsigned height, unsigned spacing) {
children.append({ 0, &widget, width, height, spacing });
}
void HorizontalLayout::setGeometry(Geometry &geometry) {
geometry.x += margin;
geometry.y += margin;
geometry.width -= margin * 2;
geometry.height -= margin * 2;
unsigned geometryWidth = width ? width : geometry.width;
unsigned geometryHeight = height ? height : geometry.height;
Geometry baseGeometry = geometry;
linear_vector<HorizontalLayout::Children> children = this->children;
unsigned minimumWidth = 0;
foreach(child, children) minimumWidth += child.width + child.spacing;
unsigned autosizeWidgets = 0;
foreach(child, children) {
if(child.width == 0) autosizeWidgets++;
}
foreach(child, children) {
if(child.width == 0) child.width = (geometryWidth - minimumWidth) / autosizeWidgets;
if(child.height == 0) child.height = geometryHeight;
}
unsigned maxHeight = 0;
foreach(child, children) {
maxHeight = max(maxHeight, child.height);
}
foreach(child, children) {
if(child.layout) {
child.layout->setGeometry(geometry);
geometry.x += child.spacing;
geometry.width -= child.spacing;
geometry.y = baseGeometry.y;
geometry.height = baseGeometry.height;
}
if(child.widget) {
child.widget->setGeometry({ geometry.x, geometry.y, child.width, child.height });
geometry.x += child.width + child.spacing;
geometry.width -= child.width + child.spacing;
}
}
geometry.y += maxHeight;
geometry.height -= maxHeight;
}
void HorizontalLayout::setMargin(unsigned margin_) {
margin = margin_;
}
unsigned HorizontalLayout::minimumWidth() {
unsigned width = margin * 2;
foreach(child, children) width += child.width + child.spacing;
return width;
}
void HorizontalLayout::setVisible(bool visible) {
foreach(child, children) {
if(child.layout) child.layout->setVisible(visible);
if(child.widget) child.widget->setVisible(visible);
}
}
HorizontalLayout::HorizontalLayout() {
margin = 0;
width = 0;
height = 0;
}

View File

@@ -0,0 +1,23 @@
struct VerticalLayout;
struct HorizontalLayout : public Layout {
void append(VerticalLayout &layout, unsigned width, unsigned height, unsigned spacing = 0);
void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
unsigned minimumWidth();
void setGeometry(Geometry &geometry);
void setMargin(unsigned margin);
void setParent(Window &parent);
void setVisible(bool visible);
HorizontalLayout();
//private:
unsigned margin;
unsigned width;
unsigned height;
struct Children {
VerticalLayout *layout;
Widget *widget;
unsigned width, height, spacing;
};
nall::linear_vector<Children> children;
};

View File

@@ -0,0 +1,88 @@
void VerticalLayout::setParent(Window &parent) {
foreach(child, children) {
if(child.layout) child.layout->setParent(parent);
if(child.widget) parent.append(*child.widget);
}
}
void VerticalLayout::append(HorizontalLayout &layout, unsigned width, unsigned height, unsigned spacing) {
layout.width = width;
layout.height = height;
children.append({ &layout, 0, width, height, spacing });
}
void VerticalLayout::append(Widget &widget, unsigned width, unsigned height, unsigned spacing) {
children.append({ 0, &widget, width, height, spacing });
}
void VerticalLayout::setGeometry(Geometry &geometry) {
geometry.x += margin;
geometry.y += margin;
geometry.width -= margin * 2;
geometry.height -= margin * 2;
unsigned geometryWidth = width ? width : geometry.width;
unsigned geometryHeight = height ? height : geometry.height;
Geometry baseGeometry = geometry;
linear_vector<VerticalLayout::Children> children = this->children;
unsigned minimumHeight = 0;
foreach(child, children) minimumHeight += child.height + child.spacing;
unsigned autosizeWidgets = 0;
foreach(child, children) {
if(child.height == 0) autosizeWidgets++;
}
foreach(child, children) {
if(child.width == 0) child.width = geometryWidth;
if(child.height == 0) child.height = (geometryHeight - minimumHeight) / autosizeWidgets;
}
unsigned maxWidth = 0;
foreach(child, children) {
maxWidth = max(maxWidth, child.width);
}
foreach(child, children) {
if(child.layout) {
child.layout->setGeometry(geometry);
geometry.x = baseGeometry.x;
geometry.width = baseGeometry.width;
geometry.y += child.spacing;
geometry.height -= child.spacing;
}
if(child.widget) {
child.widget->setGeometry({ geometry.x, geometry.y, child.width, child.height });
geometry.y += child.height + child.spacing;
geometry.height -= child.height + child.spacing;
}
}
geometry.x += maxWidth;
geometry.width -= maxWidth;
}
void VerticalLayout::setMargin(unsigned margin_) {
margin = margin_;
}
unsigned VerticalLayout::minimumHeight() {
unsigned height = margin * 2;
foreach(child, children) height += child.height + child.spacing;
return height;
}
void VerticalLayout::setVisible(bool visible) {
foreach(child, children) {
if(child.layout) child.layout->setVisible(visible);
if(child.widget) child.widget->setVisible(visible);
}
}
VerticalLayout::VerticalLayout() {
margin = 0;
width = 0;
height = 0;
}

View File

@@ -0,0 +1,23 @@
struct HorizontalLayout;
struct VerticalLayout : public Layout {
void append(HorizontalLayout &layout, unsigned width, unsigned height, unsigned spacing = 0);
void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
unsigned minimumHeight();
void setGeometry(Geometry &geometry);
void setMargin(unsigned margin);
void setParent(Window &parent);
void setVisible(bool visible);
VerticalLayout();
//private:
unsigned margin;
unsigned width;
unsigned height;
struct Children {
HorizontalLayout *layout;
Widget *widget;
unsigned width, height, spacing;
};
nall::linear_vector<Children> children;
};

224
bsnes/phoenix/core/state.hpp Executable file
View File

@@ -0,0 +1,224 @@
struct Font::State {
bool bold;
string family;
bool italic;
unsigned size;
bool underline;
State() {
bold = false;
italic = false;
size = 8;
underline = false;
}
};
struct Window::State {
bool backgroundColor;
unsigned backgroundColorRed, backgroundColorGreen, backgroundColorBlue;
bool fullScreen;
Geometry geometry;
reference_array<Layout&> layout;
reference_array<Menu&> menu;
Font *menuFont;
bool menuVisible;
bool resizable;
Font *statusFont;
string statusText;
bool statusVisible;
string title;
bool visible;
reference_array<Widget&> widget;
Font *widgetFont;
State() {
backgroundColor = false;
backgroundColorRed = 0;
backgroundColorGreen = 0;
backgroundColorBlue = 0;
fullScreen = false;
geometry = { 128, 128, 256, 256 };
menuFont = 0;
menuVisible = false;
resizable = true;
statusVisible = false;
visible = false;
widgetFont = 0;
}
};
struct Action::State {
bool enabled;
Window *parent;
bool visible;
State() {
enabled = true;
parent = 0;
visible = true;
}
};
struct Menu::State {
reference_array<Action&> action;
string text;
};
struct Item::State {
string text;
};
struct CheckItem::State {
bool checked;
string text;
State() {
checked = false;
}
};
struct RadioItem::State {
bool checked;
reference_array<RadioItem&> group;
string text;
State() {
checked = true;
}
};
struct Widget::State {
bool abstract;
bool enabled;
Font *font;
Geometry geometry;
bool visible;
State() {
abstract = false;
enabled = true;
font = 0;
geometry = { 0, 0, 0, 0 };
visible = true;
}
};
struct Button::State {
string text;
State() {
}
};
struct CheckBox::State {
bool checked;
string text;
State() {
checked = false;
}
};
struct ComboBox::State {
unsigned selection;
linear_vector<string> text;
State() {
selection = 0;
}
};
struct HexEdit::State {
unsigned columns;
unsigned length;
unsigned offset;
unsigned rows;
State() {
columns = 16;
length = 0;
offset = 0;
rows = 16;
}
};
struct HorizontalSlider::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};
struct Label::State {
string text;
};
struct LineEdit::State {
bool editable;
string text;
State() {
editable = true;
}
};
struct ListView::State {
bool checkable;
array<bool> checked;
lstring headerText;
bool headerVisible;
bool selected;
unsigned selection;
linear_vector<lstring> text;
State() {
checkable = false;
headerVisible = false;
selected = false;
selection = 0;
}
};
struct ProgressBar::State {
unsigned position;
State() {
position = 0;
}
};
struct RadioBox::State {
bool checked;
reference_array<RadioBox&> group;
string text;
State() {
checked = true;
}
};
struct TextEdit::State {
unsigned cursorPosition;
bool editable;
string text;
bool wordWrap;
State() {
cursorPosition = 0;
editable = true;
wordWrap = false;
}
};
struct VerticalSlider::State {
unsigned length;
unsigned position;
State() {
length = 101;
position = 0;
}
};

View File

@@ -0,0 +1,22 @@
static void Action_setFont(GtkWidget *widget, gpointer font) {
if(font == 0) return;
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font);
}
}
void pAction::setEnabled(bool enabled) {
gtk_widget_set_sensitive(widget, enabled);
}
void pAction::setFont(Font &font) {
Action_setFont(widget, font.p.gtkFont);
}
void pAction::setVisible(bool visible) {
gtk_widget_set_visible(widget, visible);
}
void pAction::constructor() {
}

View File

@@ -0,0 +1,22 @@
static void CheckItem_tick(CheckItem *self) {
if(self->p.locked == false && self->onTick) self->onTick();
}
bool pCheckItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
}
void pCheckItem::setChecked(bool checked) {
locked = true;
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), checked);
locked = false;
}
void pCheckItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pCheckItem::constructor() {
widget = gtk_check_menu_item_new_with_label("");
g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(CheckItem_tick), (gpointer)&checkItem);
}

View File

@@ -0,0 +1,12 @@
static void Item_tick(Item *self) {
if(self->onTick) self->onTick();
}
void pItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pItem::constructor() {
widget = gtk_menu_item_new_with_label("");
g_signal_connect_swapped(G_OBJECT(widget), "activate", G_CALLBACK(Item_tick), (gpointer)&item);
}

View File

@@ -0,0 +1,19 @@
void pMenu::append(Action &action) {
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), action.p.widget);
gtk_widget_show(action.p.widget);
}
void pMenu::setFont(Font &font) {
pAction::setFont(font);
foreach(item, menu.state.action) item.p.setFont(font);
}
void pMenu::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pMenu::constructor() {
submenu = gtk_menu_new();
widget = gtk_menu_item_new_with_label("");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu);
}

View File

@@ -0,0 +1,33 @@
static void RadioItem_tick(RadioItem *self) {
if(self->p.locked == false && self->checked() && self->onTick) self->onTick();
}
bool pRadioItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
}
void pRadioItem::setChecked() {
locked = true;
foreach(item, radioItem.state.group) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), false);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true);
locked = false;
}
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
foreach(item, group, n) {
if(n == 0) continue;
GSList *currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(group[0].p.widget));
if(currentGroup != gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item.p.widget))) {
gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item.p.widget), currentGroup);
}
}
}
void pRadioItem::setText(const string &text) {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), text);
}
void pRadioItem::constructor() {
widget = gtk_radio_menu_item_new_with_label(0, "");
g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(RadioItem_tick), (gpointer)&radioItem);
}

View File

@@ -0,0 +1,3 @@
void pSeparator::constructor() {
widget = gtk_separator_menu_item_new();
}

View File

@@ -1,13 +0,0 @@
static void Button_tick(Button *self) {
if(self->onTick) self->onTick();
}
void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_button_new_with_label(text);
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "clicked", G_CALLBACK(Button_tick), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}

View File

@@ -1,59 +0,0 @@
static void Canvas_expose(Canvas *self) {
uint32_t *rgb = self->canvas->bufferRGB;
uint32_t *bgr = self->canvas->bufferBGR;
for(unsigned y = self->object->widget->allocation.height; y; y--) {
for(unsigned x = self->object->widget->allocation.width; x; x--) {
uint32_t pixel = *rgb++;
*bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff);
}
}
gdk_draw_rgb_32_image(
self->object->widget->window,
self->object->widget->style->fg_gc[GTK_WIDGET_STATE(self->object->widget)],
0, 0, self->object->widget->allocation.width, self->object->widget->allocation.height,
GDK_RGB_DITHER_NONE, (guchar*)self->canvas->bufferBGR, self->canvas->pitch
);
}
void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
canvas->bufferRGB = new uint32_t[width * height]();
canvas->bufferBGR = new uint32_t[width * height]();
canvas->pitch = width * sizeof(uint32_t);
object->widget = gtk_drawing_area_new();
widget->parent = &parent;
GdkColor color;
color.pixel = color.red = color.green = color.blue = 0;
gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color);
gtk_widget_set_double_buffered(object->widget, false);
gtk_widget_add_events(object->widget, GDK_EXPOSURE_MASK);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
uint32_t* Canvas::buffer() {
return canvas->bufferRGB;
}
void Canvas::redraw() {
GdkRectangle rect;
rect.x = 0;
rect.y = 0;
rect.width = object->widget->allocation.width;
rect.height = object->widget->allocation.height;
gdk_window_invalidate_rect(object->widget->window, &rect, true);
}
Canvas::Canvas() {
canvas = new Canvas::Data;
canvas->bufferRGB = 0;
canvas->bufferBGR = 0;
}
Canvas::~Canvas() {
if(canvas->bufferRGB) delete[] canvas->bufferRGB;
if(canvas->bufferBGR) delete[] canvas->bufferBGR;
}

View File

@@ -1,23 +0,0 @@
static void CheckBox_tick(CheckBox *self) {
if(self->onTick && self->object->locked == false) self->onTick();
}
void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_check_button_new_with_label(text);
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
bool CheckBox::checked() {
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget));
}
void CheckBox::setChecked(bool checked) {
object->locked = true;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), checked);
object->locked = false;
}

View File

@@ -1,48 +0,0 @@
void ComboBox_change(ComboBox *self) {
if(self->object->locked == false && self->onChange) self->onChange();
}
void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_combo_box_new_text();
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(ComboBox_change), (gpointer)this);
if(*text) {
lstring list;
list.split("\n", text);
foreach(item, list) addItem(item);
}
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void ComboBox::reset() {
object->locked = true;
for(signed i = counter - 1; i >= 0; i--) {
gtk_combo_box_remove_text(GTK_COMBO_BOX(object->widget), i);
}
object->locked = false;
counter = 0;
}
void ComboBox::addItem(const string &text) {
gtk_combo_box_append_text(GTK_COMBO_BOX(object->widget), text);
if(counter++ == 0) setSelection(0);
}
unsigned ComboBox::selection() {
return gtk_combo_box_get_active(GTK_COMBO_BOX(object->widget));
}
void ComboBox::setSelection(unsigned item) {
object->locked = true;
gtk_combo_box_set_active(GTK_COMBO_BOX(object->widget), item);
object->locked = false;
}
ComboBox::ComboBox() {
counter = 0;
}

View File

@@ -1,58 +0,0 @@
static void EditBox_change(EditBox *self) {
if(self->object->locked == false && self->onChange) self->onChange();
}
void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_scrolled_window_new(0, 0);
widget->parent = &parent;
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN);
gtk_widget_set_size_request(object->widget, width, height);
object->subWidget = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), GTK_WRAP_WORD_CHAR);
gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget);
object->textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(object->subWidget));
gtk_text_buffer_set_text(object->textBuffer, text, -1);
g_signal_connect_swapped(G_OBJECT(object->textBuffer), "changed", G_CALLBACK(EditBox_change), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->subWidget);
gtk_widget_show(object->widget);
}
void EditBox::setFocused() {
gtk_widget_grab_focus(object->subWidget);
}
void EditBox::setEditable(bool editable) {
gtk_text_view_set_editable(GTK_TEXT_VIEW(object->subWidget), editable);
}
void EditBox::setWordWrap(bool wordWrap) {
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
}
string EditBox::text() {
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(object->textBuffer, &start);
gtk_text_buffer_get_end_iter(object->textBuffer, &end);
char *temp = gtk_text_buffer_get_text(object->textBuffer, &start, &end, true);
string text = temp;
g_free(temp);
return text;
}
void EditBox::setText(const string &text) {
object->locked = true;
gtk_text_buffer_set_text(object->textBuffer, text, -1);
object->locked = false;
}
void EditBox::setCursorPosition(unsigned position) {
GtkTextMark *mark = gtk_text_buffer_get_mark(object->textBuffer, "insert");
GtkTextIter iter;
gtk_text_buffer_get_end_iter(object->textBuffer, &iter);
gtk_text_iter_set_offset(&iter, min(position, gtk_text_iter_get_offset(&iter)));
gtk_text_buffer_place_cursor(object->textBuffer, &iter);
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(object->subWidget), mark);
}

View File

@@ -1,18 +1,22 @@
bool Font::create(const string &name, unsigned size, Font::Style style) {
font->font = pango_font_description_new();
pango_font_description_set_family(font->font, name);
pango_font_description_set_size(font->font, size * PANGO_SCALE);
pango_font_description_set_style(font->font, (style & Style::Italic) == Style::Italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL);
pango_font_description_set_weight(font->font, (style & Style::Bold) == Style::Bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
return true;
void pFont::setBold(bool bold) {
pango_font_description_set_weight(gtkFont, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
}
Font::Font() {
font = new Font::Data;
font->font = 0;
void pFont::setFamily(const string &family) {
pango_font_description_set_family(gtkFont, family);
}
Font::~Font() {
if(font->font) pango_font_description_free(font->font);
delete font;
void pFont::setItalic(bool italic) {
pango_font_description_set_style(gtkFont, italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL);
}
void pFont::setSize(unsigned size) {
pango_font_description_set_size(gtkFont, size * PANGO_SCALE);
}
void pFont::setUnderline(bool underline) {
}
void pFont::constructor() {
gtkFont = pango_font_description_new();
}

View File

@@ -1,49 +1,136 @@
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#include "gtk.hpp"
#define None X11None
#define Window X11Window
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <cairo.h>
#include <gdk/gdkkeysyms.h>
#undef None
#undef Window
using namespace nall;
namespace phoenix {
#include "object.cpp"
#include "settings.cpp"
#include "font.cpp"
#include "menu.cpp"
#include "widget.cpp"
#include "message-window.cpp"
#include "window.cpp"
#include "button.cpp"
#include "canvas.cpp"
#include "checkbox.cpp"
#include "combobox.cpp"
#include "editbox.cpp"
#include "hexeditor.cpp"
#include "horizontalslider.cpp"
#include "label.cpp"
#include "listbox.cpp"
#include "progressbar.cpp"
#include "radiobox.cpp"
#include "textbox.cpp"
#include "verticalslider.cpp"
#include "viewport.cpp"
#include "messagewindow.cpp"
Window Window::None;
#include "action/action.cpp"
#include "action/menu.cpp"
#include "action/separator.cpp"
#include "action/item.cpp"
#include "action/check-item.cpp"
#include "action/radio-item.cpp"
void OS::initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
#include "widget/widget.cpp"
#include "widget/button.cpp"
#include "widget/check-box.cpp"
#include "widget/combo-box.cpp"
#include "widget/hex-edit.cpp"
#include "widget/horizontal-slider.cpp"
#include "widget/label.cpp"
#include "widget/line-edit.cpp"
#include "widget/list-view.cpp"
#include "widget/progress-bar.cpp"
#include "widget/radio-box.cpp"
#include "widget/text-edit.cpp"
#include "widget/vertical-slider.cpp"
#include "widget/viewport.cpp"
Geometry pOS::availableGeometry() {
//TODO: is there a GTK+ function for this?
//should return desktopGeometry() sans panels, toolbars, docks, etc.
Geometry geometry = desktopGeometry();
return { geometry.x + 64, geometry.y + 64, geometry.width - 128, geometry.height - 128 };
}
Geometry pOS::desktopGeometry() {
return {
0, 0,
gdk_screen_get_width(gdk_screen_get_default()),
gdk_screen_get_height(gdk_screen_get_default())
};
}
static string pOS_fileDialog(bool save, Window &parent, const string &path, const lstring &filter) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
save == 0 ? "Load File" : "Save File",
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
save == 0 ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
foreach(filterItem, filter) {
GtkFileFilter *gtkFilter = gtk_file_filter_new();
gtk_file_filter_set_name(gtkFilter, filterItem);
lstring part;
part.split("(", filterItem);
part[1].rtrim<1>(")");
lstring list;
list.split(",", part[1]);
foreach(pattern, list) gtk_file_filter_add_pattern(gtkFilter, pattern);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), gtkFilter);
}
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
return name;
}
string pOS::fileLoad(Window &parent, const string &path, const lstring &filter) {
return pOS_fileDialog(0, parent, path, filter);
}
string pOS::fileSave(Window &parent, const string &path, const lstring &filter) {
return pOS_fileDialog(1, parent, path, filter);
}
string pOS::folderSelect(Window &parent, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Select Folder",
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
if(name == "") return "";
if(name.endswith("/") == false) name.append("/");
return name;
}
void pOS::main() {
gtk_main();
}
bool pOS::pendingEvents() {
return gtk_events_pending();
}
void pOS::processEvents() {
while(pendingEvents()) gtk_main_iteration_do(false);
}
void pOS::quit() {
settings.save();
gtk_main_quit();
}
void pOS::initialize() {
settings.load();
int argc = 1;
char *argv[2];
@@ -63,128 +150,3 @@ void OS::initialize() {
"class \"GtkTreeView\" style \"phoenix-gtk\"\n"
);
}
bool OS::pending() {
return gtk_events_pending();
}
void OS::run() {
while(pending()) gtk_main_iteration_do(false);
}
void OS::main() {
gtk_main();
}
void OS::quit() {
gtk_main_quit();
}
unsigned OS::desktopWidth() {
return gdk_screen_get_width(gdk_screen_get_default());
}
unsigned OS::desktopHeight() {
return gdk_screen_get_height(gdk_screen_get_default());
}
string OS::folderSelect(Window &parent, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Select Folder",
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
if(name.endswith("/") == false) name.append("/");
return name;
}
string OS::fileOpen(Window &parent, const string &filter, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Open File",
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
lstring list;
list.split("\n", filter);
foreach(item, list) {
lstring part;
part.split("\t", item);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")"));
lstring patterns;
patterns.split(",", part[1]);
foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
}
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
return name;
}
string OS::fileSave(Window &parent, const string &filter, const string &path) {
string name;
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Save File",
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(const gchar*)0
);
if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
lstring list;
list.split("\n", filter);
foreach(item, list) {
lstring part;
part.split("\t", item);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")"));
lstring patterns;
patterns.split(",", part[1]);
foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
}
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
name = temp;
g_free(temp);
}
gtk_widget_destroy(dialog);
return name;
}
}

View File

@@ -1,289 +1,351 @@
namespace phoenix {
struct Settings : public configuration {
unsigned frameGeometryX;
unsigned frameGeometryY;
unsigned frameGeometryWidth;
unsigned frameGeometryHeight;
unsigned menuGeometryHeight;
unsigned statusGeometryHeight;
struct Window;
struct Object {
Object();
Object& operator=(const Object&) = delete;
Object(const Object&) = delete;
//private:
virtual void unused();
struct Data;
Data *object;
void load();
void save();
Settings();
};
struct Geometry {
unsigned x, y;
unsigned width, height;
inline Geometry() : x(0), y(0), width(0), height(0) {}
inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
struct pFont;
struct pWindow;
struct pMenu;
struct pLayout;
struct pWidget;
struct pObject {
bool locked;
pObject() {
locked = false;
}
};
struct Font : Object {
enum class Style : unsigned {
None = 0,
Bold = 1,
Italic = 2,
};
bool create(const nall::string &name, unsigned size, Font::Style style = Style::None);
Font();
~Font();
//private:
struct Data;
Data *font;
};
inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); }
inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); }
struct Action : Object {
bool visible();
void setVisible(bool visible = true);
bool enabled();
void setEnabled(bool enabled = true);
Action();
//private:
struct Data;
Data *action;
};
struct Menu : Action {
void create(Window &parent, const nall::string &text);
void create(Menu &parent, const nall::string &text);
};
struct MenuSeparator : Action {
void create(Menu &parent);
};
struct MenuItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const nall::string &text);
};
struct MenuCheckItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const nall::string &text);
bool checked();
void setChecked(bool checked = true);
};
struct MenuRadioItem : Action {
nall::function<void ()> onTick;
void create(Menu &parent, const nall::string &text);
void create(MenuRadioItem &parent, const nall::string &text);
bool checked();
void setChecked();
private:
MenuRadioItem *first;
};
struct Widget : Object {
virtual void setFont(Font &font);
bool visible();
void setVisible(bool visible = true);
bool enabled();
void setEnabled(bool enabled = true);
virtual bool focused();
virtual void setFocused();
virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height);
Widget();
//private:
struct Data;
Data *widget;
};
struct Window : Widget {
nall::function<bool ()> onClose;
void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
bool focused();
void setFocused();
Geometry geometry();
void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height);
void setDefaultFont(Font &font);
void setFont(Font &font);
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setTitle(const nall::string &text);
void setStatusText(const nall::string &text);
void setMenuVisible(bool visible = true);
void setStatusVisible(bool visible = true);
bool fullscreen();
void setFullscreen(bool fullscreen = true);
Window();
//private:
struct Data;
Data *window;
static Window None;
};
struct Button : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
};
struct Canvas : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
uint32_t* buffer();
void redraw();
Canvas();
~Canvas();
//private:
struct Data;
Data *canvas;
};
struct CheckBox : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
bool checked();
void setChecked(bool checked = true);
};
struct ComboBox : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void reset();
void addItem(const nall::string &text);
unsigned selection();
void setSelection(unsigned item);
ComboBox();
private:
unsigned counter;
};
struct EditBox : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setFocused();
void setEditable(bool editable = true);
void setWordWrap(bool wordWrap = true);
nall::string text();
void setText(const nall::string &text);
void setCursorPosition(unsigned position);
};
struct HexEditor : Widget {
nall::function<uint8_t (unsigned)> onRead;
nall::function<void (unsigned, uint8_t)> onWrite;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
void setSize(unsigned size);
void setOffset(unsigned offset);
void setColumns(unsigned columns);
void setRows(unsigned rows);
void update();
HexEditor();
//private:
struct Data;
Data *hexEditor;
bool keyPress(unsigned scancode);
void scroll(unsigned position);
void setScroll();
void updateScroll();
unsigned cursorPosition();
void setCursorPosition(unsigned position);
};
struct HorizontalSlider : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length);
unsigned position();
void setPosition(unsigned position);
};
struct Label : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setText(const nall::string &text);
};
struct ListBox : Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
nall::function<void (unsigned)> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setFocused();
void setHeaderVisible(bool headerVisible = true);
void setCheckable(bool checkable = true);
void setFont(Font &font);
void reset();
void resizeColumnsToContent();
void addItem(const nall::string &text);
void setItem(unsigned row, const nall::string &text);
bool checked(unsigned row);
void setChecked(unsigned row, bool checked = true);
nall::optional<unsigned> selection();
void setSelection(unsigned row);
ListBox();
//private:
struct Data;
Data *listBox;
};
struct ProgressBar : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
void setPosition(unsigned position);
};
struct RadioBox : Widget {
nall::function<void ()> onTick;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
bool checked();
void setChecked();
private:
RadioBox *first;
};
struct TextBox : Widget {
nall::function<void ()> onActivate;
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = "");
void setEditable(bool editable = true);
nall::string text();
void setText(const nall::string &text);
};
struct VerticalSlider : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length);
unsigned position();
void setPosition(unsigned position);
};
struct Viewport : Widget {
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height);
uintptr_t handle();
};
struct MessageWindow : Object {
enum class Buttons : unsigned {
Ok,
OkCancel,
YesNo,
};
enum class Response : unsigned {
Ok,
Cancel,
Yes,
No,
};
static Response information(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response question(Window &parent, const nall::string &text, Buttons = Buttons::YesNo);
static Response warning(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
static Response critical(Window &parent, const nall::string &text, Buttons = Buttons::Ok);
};
struct OS : Object {
static bool pending();
static void run();
struct pOS : public pObject {
static Geometry availableGeometry();
static Geometry desktopGeometry();
static string fileLoad(Window &parent, const string &path, const lstring &filter);
static string fileSave(Window &parent, const string &path, const lstring &filter);
static string folderSelect(Window &parent, const string &path);
static void main();
static bool pendingEvents();
static void processEvents();
static void quit();
static unsigned desktopWidth();
static unsigned desktopHeight();
static nall::string folderSelect(Window &parent, const nall::string &path = "");
static nall::string fileOpen(Window &parent, const nall::string &filter, const nall::string &path = "");
static nall::string fileSave(Window &parent, const nall::string &filter, const nall::string &path = "");
//private:
static void initialize();
};
}
struct pFont : public pObject {
Font &font;
PangoFontDescription *gtkFont;
void setBold(bool bold);
void setFamily(const string &family);
void setItalic(bool italic);
void setSize(unsigned size);
void setUnderline(bool underline);
pFont(Font &font) : font(font) {}
void constructor();
};
struct pMessageWindow : public pObject {
static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons);
static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons);
static MessageWindow::Response warning(Window &parent, const string &text, MessageWindow::Buttons buttons);
static MessageWindow::Response critical(Window &parent, const string &text, MessageWindow::Buttons buttons);
};
struct pWindow : public pObject {
Window &window;
GtkWidget *widget;
GtkWidget *menuContainer;
GtkWidget *formContainer;
GtkWidget *statusContainer;
GtkWidget *menu;
GtkWidget *status;
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
bool focused();
Geometry frameMargin();
Geometry geometry();
void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
void setFocused();
void setFullScreen(bool fullScreen);
void setGeometry(const Geometry &geometry);
void setMenuFont(Font &font);
void setMenuVisible(bool visible);
void setResizable(bool resizable);
void setStatusFont(Font &font);
void setStatusText(const string &text);
void setStatusVisible(bool visible);
void setTitle(const string &text);
void setVisible(bool visible);
void setWidgetFont(Font &font);
pWindow(Window &window) : window(window) {}
void constructor();
unsigned menuHeight();
unsigned statusHeight();
};
struct pAction : public pObject {
Action &action;
GtkWidget *widget;
void setEnabled(bool enabled);
void setVisible(bool visible);
pAction(Action &action) : action(action) {}
void constructor();
virtual void setFont(Font &font);
};
struct pMenu : public pAction {
Menu &menu;
GtkWidget *submenu;
void append(Action &action);
void setText(const string &text);
pMenu(Menu &menu) : pAction(menu), menu(menu) {}
void constructor();
void setFont(Font &font);
};
struct pSeparator : public pAction {
Separator &separator;
pSeparator(Separator &separator) : pAction(separator), separator(separator) {}
void constructor();
};
struct pItem : public pAction {
Item &item;
void setText(const string &text);
pItem(Item &item) : pAction(item), item(item) {}
void constructor();
};
struct pCheckItem : public pAction {
CheckItem &checkItem;
bool checked();
void setChecked(bool checked);
void setText(const string &text);
pCheckItem(CheckItem &checkItem) : pAction(checkItem), checkItem(checkItem) {}
void constructor();
};
struct pRadioItem : public pAction {
RadioItem &radioItem;
bool checked();
void setChecked();
void setGroup(const reference_array<RadioItem&> &group);
void setText(const string &text);
pRadioItem(RadioItem &radioItem) : pAction(radioItem), radioItem(radioItem) {}
void constructor();
};
struct pWidget : public pObject {
Widget &widget;
GtkWidget *gtkWidget;
pWindow *parentWindow;
bool enabled();
void setEnabled(bool enabled);
virtual void setFocused();
virtual void setFont(Font &font);
void setGeometry(const Geometry &geometry);
void setVisible(bool visible);
pWidget(Widget &widget) : widget(widget) {}
void constructor();
};
struct pButton : public pWidget {
Button &button;
void setText(const string &text);
pButton(Button &button) : pWidget(button), button(button) {}
void constructor();
};
struct pCheckBox : public pWidget {
CheckBox &checkBox;
bool checked();
void setChecked(bool checked);
void setText(const string &text);
pCheckBox(CheckBox &checkBox) : pWidget(checkBox), checkBox(checkBox) {}
void constructor();
};
struct pComboBox : public pWidget {
ComboBox &comboBox;
unsigned itemCounter;
void append(const string &text);
void reset();
unsigned selection();
void setSelection(unsigned row);
pComboBox(ComboBox &comboBox) : pWidget(comboBox), comboBox(comboBox) {}
void constructor();
};
struct pHexEdit : public pWidget {
HexEdit &hexEdit;
GtkWidget *container;
GtkWidget *subWidget;
GtkWidget *scrollBar;
GtkTextBuffer *textBuffer;
GtkTextMark *textCursor;
void setColumns(unsigned columns);
void setLength(unsigned length);
void setOffset(unsigned offset);
void setRows(unsigned rows);
void update();
pHexEdit(HexEdit &hexEdit) : pWidget(hexEdit), hexEdit(hexEdit) {}
void constructor();
unsigned cursorPosition();
bool keyPress(unsigned scancode);
void scroll(unsigned position);
void setCursorPosition(unsigned position);
void setScroll();
void updateScroll();
};
struct pHorizontalSlider : public pWidget {
HorizontalSlider &horizontalSlider;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
pHorizontalSlider(HorizontalSlider &horizontalSlider) : pWidget(horizontalSlider), horizontalSlider(horizontalSlider) {}
void constructor();
};
struct pLabel : public pWidget {
Label &label;
void setText(const string &text);
pLabel(Label &label) : pWidget(label), label(label) {}
void constructor();
};
struct pLineEdit : public pWidget {
LineEdit &lineEdit;
void setEditable(bool editable);
void setText(const string &text);
string text();
pLineEdit(LineEdit &lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {}
void constructor();
};
struct pListView : public pWidget {
ListView &listView;
GtkWidget *subWidget;
GtkListStore *store;
struct GtkColumn {
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *label;
};
linear_vector<GtkColumn> column;
void append(const lstring &text);
void autoSizeColumns();
bool checked(unsigned row);
void modify(unsigned row, const lstring &text);
void reset();
bool selected();
unsigned selection();
void setCheckable(bool checkable);
void setChecked(unsigned row, bool checked);
void setHeaderText(const lstring &text);
void setHeaderVisible(bool visible);
void setSelected(bool selected);
void setSelection(unsigned row);
pListView(ListView &listView) : pWidget(listView), listView(listView) {}
void constructor();
void create();
void setFocused();
void setFont(Font &font);
};
struct pProgressBar : public pWidget {
ProgressBar &progressBar;
void setPosition(unsigned position);
pProgressBar(ProgressBar &progressBar) : pWidget(progressBar), progressBar(progressBar) {}
void constructor();
};
struct pRadioBox : public pWidget {
RadioBox &radioBox;
bool checked();
void setChecked();
void setGroup(const reference_array<RadioBox&> &group);
void setText(const string &text);
pRadioBox(RadioBox &radioBox) : pWidget(radioBox), radioBox(radioBox) {}
void constructor();
};
struct pTextEdit : public pWidget {
TextEdit &textEdit;
GtkWidget *subWidget;
GtkTextBuffer *textBuffer;
void setCursorPosition(unsigned position);
void setEditable(bool editable);
void setText(const string &text);
void setWordWrap(bool wordWrap);
string text();
pTextEdit(TextEdit &textEdit) : pWidget(textEdit), textEdit(textEdit) {}
void constructor();
};
struct pVerticalSlider : public pWidget {
VerticalSlider &verticalSlider;
unsigned position();
void setLength(unsigned length);
void setPosition(unsigned position);
pVerticalSlider(VerticalSlider &verticalSlider) : pWidget(verticalSlider), verticalSlider(verticalSlider) {}
void constructor();
};
struct pViewport : public pWidget {
Viewport &viewport;
uintptr_t handle();
pViewport(Viewport &viewport) : pWidget(viewport), viewport(viewport) {}
void constructor();
};

View File

@@ -1,266 +0,0 @@
static bool HexEditor_keyPress(GtkWidget *widget, GdkEventKey *event, HexEditor *self) {
return self->keyPress(event->keyval);
}
static bool HexEditor_scroll(GtkRange *range, GtkScrollType scroll, gdouble value, HexEditor *self) {
self->scroll((unsigned)value);
return false;
}
void HexEditor::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
widget->parent = &parent;
hexEditor->size = 0;
hexEditor->offset = 0;
hexEditor->columns = 16;
hexEditor->rows = 16;
object->widget = gtk_hbox_new(false, 0);
gtk_widget_set_size_request(object->widget, width, height);
hexEditor->container = gtk_scrolled_window_new(0, 0);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(hexEditor->container), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(hexEditor->container), GTK_SHADOW_ETCHED_IN);
hexEditor->widget = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(hexEditor->widget), GTK_WRAP_NONE);
gtk_container_add(GTK_CONTAINER(hexEditor->container), hexEditor->widget);
g_signal_connect(G_OBJECT(hexEditor->widget), "key-press-event", G_CALLBACK(HexEditor_keyPress), (gpointer)this);
hexEditor->scroll = gtk_vscrollbar_new((GtkAdjustment*)0);
gtk_range_set_range(GTK_RANGE(hexEditor->scroll), 0, 256);
gtk_range_set_increments(GTK_RANGE(hexEditor->scroll), 1, 16);
gtk_widget_set_sensitive(hexEditor->scroll, false);
g_signal_connect(G_OBJECT(hexEditor->scroll), "change-value", G_CALLBACK(HexEditor_scroll), (gpointer)this);
gtk_box_pack_start(GTK_BOX(object->widget), hexEditor->container, true, true, 0);
gtk_box_pack_start(GTK_BOX(object->widget), hexEditor->scroll, false, false, 1);
object->textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(hexEditor->widget));
hexEditor->cursor = gtk_text_buffer_get_mark(object->textBuffer, "insert");
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_widget_show(hexEditor->scroll);
gtk_widget_show(hexEditor->widget);
gtk_widget_show(hexEditor->container);
gtk_widget_show(object->widget);
}
void HexEditor::setSize(unsigned size) {
hexEditor->size = size;
setScroll();
}
void HexEditor::setOffset(unsigned offset) {
hexEditor->offset = offset;
setScroll();
updateScroll();
}
void HexEditor::setColumns(unsigned columns) {
hexEditor->columns = columns;
setScroll();
}
void HexEditor::setRows(unsigned rows) {
hexEditor->rows = rows;
setScroll();
}
void HexEditor::update() {
if(!onRead) {
gtk_text_buffer_set_text(object->textBuffer, "", -1);
return;
}
unsigned position = cursorPosition();
string output;
unsigned offset = hexEditor->offset;
for(unsigned row = 0; row < hexEditor->rows; row++) {
output.append(hex<8>(offset));
output.append(" ");
string hexdata;
string ansidata = " ";
for(unsigned column = 0; column < hexEditor->columns; column++) {
if(offset < hexEditor->size) {
uint8_t data = onRead(offset++);
hexdata.append(hex<2>(data));
hexdata.append(" ");
char buffer[2] = { data >= 0x20 && data <= 0x7e ? (char)data : '.', 0 };
ansidata.append(buffer);
} else {
hexdata.append(" ");
ansidata.append(" ");
}
}
output.append(hexdata);
output.append(ansidata);
if(offset >= hexEditor->size) break;
if(row != hexEditor->rows - 1) output.append("\n");
}
gtk_text_buffer_set_text(object->textBuffer, output, -1);
if(position == 0) position = 10; //start at first position where hex values can be entered
setCursorPosition(position);
}
HexEditor::HexEditor() {
hexEditor = new HexEditor::Data;
}
//internal
bool HexEditor::keyPress(unsigned scancode) {
if(!onRead && !onWrite) return false;
unsigned position = cursorPosition();
unsigned lineWidth = 10 + (hexEditor->columns * 3) + 1 + (hexEditor->columns) + 1;
unsigned cursorY = position / lineWidth;
unsigned cursorX = position % lineWidth;
if(scancode == GDK_Home) {
setCursorPosition(cursorY * lineWidth + 10);
return true;
}
if(scancode == GDK_End) {
setCursorPosition(cursorY * lineWidth + 10 + (hexEditor->columns * 3 - 1));
return true;
}
if(scancode == GDK_Up) {
if(cursorY != 0) return false;
signed newOffset = hexEditor->offset - hexEditor->columns;
if(newOffset >= 0) {
setOffset(newOffset);
update();
}
return true;
}
if(scancode == GDK_Down) {
if(cursorY != hexEditor->rows - 1) return false;
signed newOffset = hexEditor->offset + hexEditor->columns;
if(newOffset + hexEditor->columns * hexEditor->rows - (hexEditor->columns - 1) <= hexEditor->size) {
setOffset(newOffset);
update();
}
return true;
}
if(scancode == GDK_Page_Up) {
signed newOffset = hexEditor->offset - hexEditor->columns * hexEditor->rows;
if(newOffset >= 0) {
setOffset(newOffset);
update();
} else {
setOffset(0);
update();
}
return true;
}
if(scancode == GDK_Page_Down) {
signed newOffset = hexEditor->offset + hexEditor->columns * hexEditor->rows;
for(unsigned n = 0; n < hexEditor->rows; n++) {
if(newOffset + hexEditor->columns * hexEditor->rows - (hexEditor->columns - 1) <= hexEditor->size) {
setOffset(newOffset);
update();
break;
}
newOffset -= hexEditor->columns;
}
return true;
}
//convert scancode to hex nibble
if(scancode >= '0' && scancode <= '9') scancode = scancode - '0';
else if(scancode >= 'A' && scancode <= 'F') scancode = scancode - 'A' + 10;
else if(scancode >= 'a' && scancode <= 'f') scancode = scancode - 'a' + 10;
else return false; //not a valid hex value
if(cursorX >= 10) {
//not on an offset
cursorX -= 10;
if((cursorX % 3) != 2) {
//not on a space
bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low
cursorX /= 3;
if(cursorX < hexEditor->columns) {
//not in ANSI region
unsigned offset = hexEditor->offset + (cursorY * hexEditor->columns + cursorX);
if(offset >= hexEditor->size) return false; //do not edit past end of file
uint8_t data = onRead(offset);
//write modified value
if(cursorNibble == 1) {
data = (data & 0xf0) | (scancode << 0);
} else {
data = (data & 0x0f) | (scancode << 4);
}
onWrite(offset, data);
//auto-advance cursor to next nibble/byte
position++;
if(cursorNibble && cursorX != hexEditor->columns - 1) position++;
setCursorPosition(position);
//refresh output to reflect modified data
update();
}
}
}
return true;
}
void HexEditor::scroll(unsigned position) {
unsigned rows = hexEditor->size / hexEditor->columns;
if(position >= rows) position = rows - 1;
setOffset(position * hexEditor->columns);
update();
}
void HexEditor::setScroll() {
unsigned rows = hexEditor->size / hexEditor->columns;
if(rows) rows--;
if(rows) {
gtk_range_set_range(GTK_RANGE(hexEditor->scroll), 0, rows);
gtk_widget_set_sensitive(hexEditor->scroll, true);
} else {
gtk_widget_set_sensitive(hexEditor->scroll, false);
}
}
void HexEditor::updateScroll() {
unsigned row = hexEditor->offset / hexEditor->columns;
gtk_range_set_value(GTK_RANGE(hexEditor->scroll), row);
}
unsigned HexEditor::cursorPosition() {
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(object->textBuffer, &iter, hexEditor->cursor);
return gtk_text_iter_get_offset(&iter);
}
void HexEditor::setCursorPosition(unsigned position) {
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(object->textBuffer, &iter, hexEditor->cursor);
//GTK+ will throw a hundred errors on the terminal
//if you set an iterator past the end of the text buffer
GtkTextIter endIter;
gtk_text_buffer_get_end_iter(object->textBuffer, &iter);
unsigned endPosition = gtk_text_iter_get_offset(&iter);
gtk_text_iter_set_offset(&iter, min(position, endPosition));
gtk_text_buffer_place_cursor(object->textBuffer, &iter);
}

View File

@@ -1,25 +0,0 @@
static void HorizontalSlider_change(HorizontalSlider *self) {
if(self->object->position == self->position()) return;
self->object->position = self->position();
if(self->onChange) self->onChange();
}
void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) {
object->position = 0;
length += (length == 0);
object->widget = gtk_hscale_new_with_range(0, length - 1, 1);
widget->parent = &parent;
gtk_scale_set_draw_value(GTK_SCALE(object->widget), false);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
unsigned HorizontalSlider::position() {
return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget));
}
void HorizontalSlider::setPosition(unsigned position) {
gtk_range_set_value(GTK_RANGE(object->widget), position);
}

View File

@@ -1,13 +0,0 @@
void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_label_new(text);
widget->parent = &parent;
gtk_misc_set_alignment(GTK_MISC(object->widget), 0.0, 0.5);
gtk_widget_set_size_request(object->widget, width, height);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void Label::setText(const string &text) {
gtk_label_set_text(GTK_LABEL(object->widget), text);
}

View File

@@ -1,195 +0,0 @@
static void ListBox_activate(ListBox *self) {
signed selection = -1;
if(auto position = self->selection()) selection = position();
self->listBox->selection = selection;
if(self->onActivate) self->onActivate();
}
static void ListBox_change(ListBox *self) {
signed selection = -1;
if(auto position = self->selection()) selection = position();
if(selection == self->listBox->selection) return;
self->listBox->selection = selection;
if(self->onChange) self->onChange();
}
static void ListBox_tick(GtkCellRendererToggle *cell, gchar *path_string, ListBox *self) {
unsigned index = decimal(path_string);
self->setChecked(index, !self->checked(index));
if(self->onTick) self->onTick(index);
}
void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
listBox->selection = -1;
object->widget = gtk_scrolled_window_new(0, 0);
widget->parent = &parent;
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN);
gtk_widget_set_size_request(object->widget, width, height);
lstring list;
list.split("\t", string("\t", text));
GType *v = (GType*)malloc(list.size() * sizeof(GType));
for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING);
listBox->store = gtk_list_store_newv(list.size(), v);
free(v);
object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store));
gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget);
g_object_unref(G_OBJECT(listBox->store));
for(unsigned i = 0; i < list.size(); i++) {
if(i == 0) {
listBox->column[i].renderer = gtk_cell_renderer_toggle_new();
listBox->column[i].column = gtk_tree_view_column_new_with_attributes(
"", listBox->column[i].renderer, "active", i, (void*)0
);
gtk_tree_view_column_set_resizable(listBox->column[i].column, false);
gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable);
g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this);
} else {
listBox->column[i].renderer = gtk_cell_renderer_text_new();
listBox->column[i].column = gtk_tree_view_column_new_with_attributes(
"", listBox->column[i].renderer, "text", i, (void*)0
);
gtk_tree_view_column_set_resizable(listBox->column[i].column, true);
}
listBox->column[i].label = gtk_label_new(list[i]);
gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label);
gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column);
gtk_widget_show(listBox->column[i].label);
}
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false);
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column
gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1);
g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this);
g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->subWidget);
gtk_widget_show(object->widget);
}
void ListBox::setFocused() {
gtk_widget_grab_focus(object->subWidget);
}
void ListBox::setHeaderVisible(bool visible) {
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), visible);
}
void ListBox::setCheckable(bool checkable) {
listBox->checkable = checkable;
if(object->subWidget) gtk_tree_view_column_set_visible(listBox->column[0].column, checkable);
}
void ListBox::setFont(Font &font) {
Widget::setFont(font);
unsigned columns = 1;
while(true) {
if(gtk_tree_view_get_column(GTK_TREE_VIEW(object->subWidget), columns) == 0) break;
columns++;
}
for(unsigned i = 0; i < columns; i++) {
gtk_widget_modify_font(listBox->column[i].label, font.font->font);
}
}
void ListBox::reset() {
listBox->selection = -1;
gtk_list_store_clear(GTK_LIST_STORE(listBox->store));
gtk_tree_view_set_model(GTK_TREE_VIEW(object->subWidget), GTK_TREE_MODEL(listBox->store));
//reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListBox is now empty
gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(object->widget), 0);
gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(object->widget), 0);
}
void ListBox::resizeColumnsToContent() {
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(object->subWidget));
}
void ListBox::addItem(const string &text) {
lstring list;
list.split("\t", text);
GtkTreeIter iter;
gtk_list_store_append(listBox->store, &iter);
unsigned index = 1;
foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1);
}
void ListBox::setItem(unsigned row, const string &text) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreeIter iter;
for(unsigned i = 0; i <= row; i++) {
if(i == 0) gtk_tree_model_get_iter_first(model, &iter);
else gtk_tree_model_iter_next(model, &iter);
}
lstring list;
list.split("\t", text);
unsigned index = 1;
foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1);
}
bool ListBox::checked(unsigned row) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreePath *path = gtk_tree_path_new_from_string(string(row));
GtkTreeIter iter;
bool state;
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, 0, &state, -1);
gtk_tree_path_free(path);
return state;
}
void ListBox::setChecked(unsigned row, bool checked) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreePath *path = gtk_tree_path_new_from_string(string(row));
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, path);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1);
gtk_tree_path_free(path);
}
optional<unsigned> ListBox::selection() {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter) == false) return { false, 0 };
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, 0 };
for(unsigned i = 1;; i++) {
if(gtk_tree_model_iter_next(model, &iter) == false) return { false, 0 };
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, i };
}
return { false, 0 };
}
void ListBox::setSelection(unsigned row) {
signed current = -1;
if(auto position = selection()) current = position();
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget));
gtk_tree_selection_unselect_all(selection);
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter) == false) return;
if(row == 0) {
gtk_tree_selection_select_iter(selection, &iter);
return;
}
for(unsigned i = 1;; i++) {
if(gtk_tree_model_iter_next(model, &iter) == false) return;
if(row == i) {
gtk_tree_selection_select_iter(selection, &iter);
return;
}
}
}
ListBox::ListBox() {
listBox = new ListBox::Data;
listBox->checkable = false;
}

View File

@@ -1,129 +0,0 @@
static void Action_setFont(GtkWidget *widget, gpointer font) {
if(font) {
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, (PangoFontDescription*)font);
}
}
}
bool Action::visible() {
return gtk_widget_get_visible(object->widget);
}
void Action::setVisible(bool visible) {
gtk_widget_set_visible(object->widget, visible);
}
bool Action::enabled() {
return gtk_widget_get_sensitive(object->widget);
}
void Action::setEnabled(bool enabled) {
gtk_widget_set_sensitive(object->widget, enabled);
}
Action::Action() {
action = new Action::Data;
action->font = 0;
}
void Menu::create(Window &parent, const string &text) {
action->font = parent.window->defaultFont;
object->menu = gtk_menu_new();
object->widget = gtk_menu_item_new_with_label(text);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_bar_append(parent.object->menu, object->widget);
gtk_widget_show(object->widget);
}
void Menu::create(Menu &parent, const string &text) {
action->font = parent.action->font;
object->menu = gtk_menu_new();
object->widget = gtk_menu_item_new_with_label(text);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
void MenuSeparator::create(Menu &parent) {
action->font = parent.action->font;
object->widget = gtk_separator_menu_item_new();
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
static void MenuItem_tick(MenuItem *self) {
if(self->onTick) self->onTick();
}
void MenuItem::create(Menu &parent, const string &text) {
action->font = parent.action->font;
object->widget = gtk_menu_item_new_with_label(text);
g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(MenuItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
static void MenuCheckItem_tick(MenuCheckItem *self) {
if(self->onTick && self->object->locked == false) self->onTick();
}
void MenuCheckItem::create(Menu &parent, const string &text) {
action->font = parent.action->font;
object->widget = gtk_check_menu_item_new_with_label(text);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuCheckItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
bool MenuCheckItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget));
}
void MenuCheckItem::setChecked(bool state) {
object->locked = true;
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), state);
object->locked = false;
}
static void MenuRadioItem_tick(MenuRadioItem *self) {
if(self->onTick && self->checked() && self->object->locked == false) self->onTick();
}
void MenuRadioItem::create(Menu &parent, const string &text) {
first = this;
action->font = parent.action->font;
object->parentMenu = &parent;
object->widget = gtk_radio_menu_item_new_with_label(0, text);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget);
gtk_widget_show(object->widget);
}
void MenuRadioItem::create(MenuRadioItem &parent, const string &text) {
first = parent.first;
action->font = parent.action->font;
object->parentMenu = parent.object->parentMenu;
object->widget = gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(first->object->widget), text);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this);
if(action->font) Action_setFont(object->widget, action->font->font->font);
gtk_menu_shell_append(GTK_MENU_SHELL(object->parentMenu->object->menu), object->widget);
gtk_widget_show(object->widget);
}
bool MenuRadioItem::checked() {
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget));
}
void MenuRadioItem::setChecked() {
object->locked = true;
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), true);
object->locked = false;
}

View File

@@ -8,13 +8,12 @@ static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons but
return MessageWindow::Response::Ok;
}
MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -22,13 +21,12 @@ MessageWindow::Response MessageWindow::information(Window &parent, const string
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -36,13 +34,12 @@ MessageWindow::Response MessageWindow::question(Window &parent, const string &te
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -50,13 +47,12 @@ MessageWindow::Response MessageWindow::warning(Window &parent, const string &tex
return MessageWindow_response(buttons, response);
}
MessageWindow::Response MessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) {
MessageWindow::Response pMessageWindow::critical(Window &parent, const string &text, MessageWindow::Buttons buttons) {
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
GtkWidget *dialog = gtk_message_dialog_new(
&parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0,
&parent != &Window::None ? GTK_WINDOW(parent.p.widget) : (GtkWindow*)0,
GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", (const char*)text
);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));

View File

@@ -1,74 +0,0 @@
struct Object::Data {
bool locked;
GtkWidget *widget;
GtkWidget *subWidget;
GtkWidget *menuContainer;
GtkWidget *formContainer;
GtkWidget *statusContainer;
GtkWidget *menu;
GtkWidget *status;
Menu *parentMenu;
Window *parentWindow;
GtkTextBuffer *textBuffer;
unsigned position;
};
struct Font::Data {
PangoFontDescription *font;
};
struct Action::Data {
Font *font;
};
struct Widget::Data {
Window *parent;
};
struct Window::Data {
Font *defaultFont;
bool isFullscreen;
unsigned x;
unsigned y;
unsigned width;
unsigned height;
};
struct Canvas::Data {
uint32_t *bufferRGB;
uint32_t *bufferBGR;
unsigned pitch;
};
struct HexEditor::Data {
GtkWidget *container;
GtkWidget *widget;
GtkWidget *scroll;
GtkTextMark *cursor;
unsigned size;
unsigned offset;
unsigned columns;
unsigned rows;
};
struct ListBox::Data {
GtkListStore *store;
struct GtkColumn {
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *label;
};
linear_vector<GtkColumn> column;
bool checkable;
signed selection;
};
void Object::unused() {
}
Object::Object() {
OS::initialize();
object = new Object::Data;
object->locked = false;
}

View File

@@ -1,12 +0,0 @@
void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
object->widget = gtk_progress_bar_new();
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void ProgressBar::setPosition(unsigned position) {
position = position <= 100 ? position : 0;
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)position / 100.0);
}

View File

@@ -1,36 +0,0 @@
static void RadioBox_tick(RadioBox *self) {
if(self->onTick && self->checked() && self->object->locked == false) self->onTick();
}
void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
first = this;
object->parentWindow = &parent;
object->widget = gtk_radio_button_new_with_label(0, text);
widget->parent = &parent;
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
first = parent.first;
object->parentWindow = parent.object->parentWindow;
object->widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(parent.object->widget), text);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this);
if(object->parentWindow->window->defaultFont) setFont(*object->parentWindow->window->defaultFont);
gtk_fixed_put(GTK_FIXED(object->parentWindow->object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
bool RadioBox::checked() {
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget));
}
void RadioBox::setChecked() {
object->locked = true;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), true);
object->locked = false;
}

24
bsnes/phoenix/gtk/settings.cpp Executable file
View File

@@ -0,0 +1,24 @@
static Settings settings;
void Settings::load() {
string path = { userpath(), ".config/phoenix/gtk.cfg" };
configuration::load(path);
}
void Settings::save() {
string path = { userpath(), ".config/" };
mkdir(path, 0755);
path.append("phoenix/");
mkdir(path, 0755);
path.append("gtk.cfg");
configuration::save(path);
}
Settings::Settings() {
attach(frameGeometryX = 4, "frameGeometryX");
attach(frameGeometryY = 24, "frameGeometryY");
attach(frameGeometryWidth = 8, "frameGeometryWidth");
attach(frameGeometryHeight = 28, "frameGeometryHeight");
attach(menuGeometryHeight = 20, "menuGeometryHeight");
attach(statusGeometryHeight = 20, "statusGeometryHeight");
}

View File

@@ -1,33 +0,0 @@
static void TextBox_activate(TextBox *self) {
if(self->onActivate) self->onActivate();
}
static void TextBox_change(TextBox *self) {
if(self->object->locked == false && self->onChange) self->onChange();
}
void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
object->widget = gtk_entry_new();
widget->parent = &parent;
gtk_entry_set_text(GTK_ENTRY(object->widget), text);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(TextBox_activate), (gpointer)this);
g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(TextBox_change), (gpointer)this);
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
void TextBox::setEditable(bool editable) {
gtk_entry_set_editable(GTK_ENTRY(object->widget), editable);
}
string TextBox::text() {
return gtk_entry_get_text(GTK_ENTRY(object->widget));
}
void TextBox::setText(const string &text) {
object->locked = true;
gtk_entry_set_text(GTK_ENTRY(object->widget), text);
object->locked = false;
}

View File

@@ -1,25 +0,0 @@
static void VerticalSlider_change(VerticalSlider *self) {
if(self->object->position == self->position()) return;
self->object->position = self->position();
if(self->onChange) self->onChange();
}
void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) {
object->position = 0;
length += (length == 0);
object->widget = gtk_vscale_new_with_range(0, length - 1, 1);
widget->parent = &parent;
gtk_scale_set_draw_value(GTK_SCALE(object->widget), false);
gtk_widget_set_size_request(object->widget, width, height);
g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)this);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
unsigned VerticalSlider::position() {
return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget));
}
void VerticalSlider::setPosition(unsigned position) {
gtk_range_set_value(GTK_RANGE(object->widget), position);
}

View File

@@ -1,20 +0,0 @@
void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
object->widget = gtk_drawing_area_new();
widget->parent = &parent;
//gtk_widget_set_double_buffered(object->widget, false);
gtk_widget_set_size_request(object->widget, width, height);
GdkColor color;
color.pixel = 0;
color.red = 0;
color.green = 0;
color.blue = 0;
gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color);
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
gtk_widget_show(object->widget);
}
uintptr_t Viewport::handle() {
return GDK_WINDOW_XID(object->widget->window);
}

View File

@@ -1,47 +0,0 @@
static void Widget_setFont(GtkWidget *widget, gpointer font) {
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Widget_setFont, font);
}
}
void Widget::setFont(Font &font) {
Widget_setFont(object->widget, font.font->font);
}
bool Widget::visible() {
return gtk_widget_get_visible(object->widget);
}
void Widget::setVisible(bool visible) {
if(visible) gtk_widget_show(object->widget);
else gtk_widget_hide(object->widget);
}
bool Widget::enabled() {
return gtk_widget_get_sensitive(object->widget);
}
void Widget::setEnabled(bool enabled) {
gtk_widget_set_sensitive(object->widget, enabled);
}
bool Widget::focused() {
return gtk_widget_is_focus(object->widget);
}
void Widget::setFocused() {
if(visible() == false) setVisible(true);
gtk_widget_grab_focus(object->widget);
}
void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
if(widget->parent == 0) return;
gtk_fixed_move(GTK_FIXED(widget->parent->object->formContainer), object->widget, x, y);
gtk_widget_set_size_request(object->widget, width, height);
}
Widget::Widget() {
widget = new Widget::Data;
widget->parent = 0;
}

View File

@@ -0,0 +1,12 @@
static void Button_tick(Button *self) {
if(self->onTick) self->onTick();
}
void pButton::setText(const string &text) {
gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
}
void pButton::constructor() {
gtkWidget = gtk_button_new();
g_signal_connect_swapped(G_OBJECT(gtkWidget), "clicked", G_CALLBACK(Button_tick), (gpointer)&button);
}

View File

@@ -0,0 +1,22 @@
static void CheckBox_tick(CheckBox *self) {
if(self->p.locked == false && self->onTick) self->onTick();
}
bool pCheckBox::checked() {
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget));
}
void pCheckBox::setChecked(bool checked) {
locked = true;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), checked);
locked = false;
}
void pCheckBox::setText(const string &text) {
gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
}
void pCheckBox::constructor() {
gtkWidget = gtk_check_button_new_with_label("");
g_signal_connect_swapped(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)&checkBox);
}

View File

@@ -0,0 +1,33 @@
static void ComboBox_change(ComboBox *self) {
if(self->p.locked == false && self->onChange) self->onChange();
}
void pComboBox::append(const string &text) {
gtk_combo_box_append_text(GTK_COMBO_BOX(gtkWidget), text);
if(itemCounter++ == 0) setSelection(0);
}
void pComboBox::reset() {
locked = true;
for(signed n = itemCounter - 1; n >= 0; n--) {
gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), n);
}
itemCounter = 0;
locked = false;
}
unsigned pComboBox::selection() {
return gtk_combo_box_get_active(GTK_COMBO_BOX(gtkWidget));
}
void pComboBox::setSelection(unsigned row) {
locked = true;
gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), row);
locked = false;
}
void pComboBox::constructor() {
itemCounter = 0;
gtkWidget = gtk_combo_box_new_text();
g_signal_connect_swapped(G_OBJECT(gtkWidget), "changed", G_CALLBACK(ComboBox_change), (gpointer)&comboBox);
}

View File

@@ -0,0 +1,246 @@
static bool HexEdit_keyPress(GtkWidget *widget, GdkEventKey *event, HexEdit *self) {
return self->p.keyPress(event->keyval);
}
static bool HexEdit_scroll(GtkRange *range, GtkScrollType scroll, gdouble value, HexEdit *self) {
self->p.scroll((unsigned)value);
return false;
}
void pHexEdit::setColumns(unsigned columns) {
setScroll();
update();
}
void pHexEdit::setLength(unsigned length) {
setScroll();
update();
}
void pHexEdit::setOffset(unsigned offset) {
setScroll();
updateScroll();
update();
}
void pHexEdit::setRows(unsigned rows) {
setScroll();
update();
}
void pHexEdit::update() {
if(!hexEdit.onRead) {
gtk_text_buffer_set_text(textBuffer, "", -1);
return;
}
unsigned position = cursorPosition();
string output;
unsigned offset = hexEdit.state.offset;
for(unsigned row = 0; row < hexEdit.state.rows; row++) {
output.append(hex<8>(offset));
output.append(" ");
string hexdata;
string ansidata = " ";
for(unsigned column = 0; column < hexEdit.state.columns; column++) {
if(offset < hexEdit.state.length) {
uint8_t data = hexEdit.onRead(offset++);
hexdata.append(hex<2>(data));
hexdata.append(" ");
char buffer[2] = { data >= 0x20 && data <= 0x7e ? (char)data : '.', 0 };
ansidata.append(buffer);
} else {
hexdata.append(" ");
ansidata.append(" ");
}
}
output.append(hexdata);
output.append(ansidata);
if(offset >= hexEdit.state.length) break;
if(row != hexEdit.state.rows - 1) output.append("\n");
}
gtk_text_buffer_set_text(textBuffer, output, -1);
if(position == 0) position = 10; //start at first position where hex values can be entered
setCursorPosition(position);
}
void pHexEdit::constructor() {
gtkWidget = gtk_hbox_new(false, 0);
container = gtk_scrolled_window_new(0, 0);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(container), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(container), GTK_SHADOW_ETCHED_IN);
subWidget = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), false);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_NONE);
gtk_container_add(GTK_CONTAINER(container), subWidget);
g_signal_connect(G_OBJECT(subWidget), "key-press-event", G_CALLBACK(HexEdit_keyPress), (gpointer)&hexEdit);
scrollBar = gtk_vscrollbar_new((GtkAdjustment*)0);
gtk_range_set_range(GTK_RANGE(scrollBar), 0, 255);
gtk_range_set_increments(GTK_RANGE(scrollBar), 1, 16);
gtk_widget_set_sensitive(scrollBar, false);
g_signal_connect(G_OBJECT(scrollBar), "change-value", G_CALLBACK(HexEdit_scroll), (gpointer)&hexEdit);
gtk_box_pack_start(GTK_BOX(gtkWidget), container, true, true, 0);
gtk_box_pack_start(GTK_BOX(gtkWidget), scrollBar, false, false, 1);
textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
textCursor = gtk_text_buffer_get_mark(textBuffer, "insert");
gtk_widget_show(scrollBar);
gtk_widget_show(subWidget);
gtk_widget_show(container);
}
unsigned pHexEdit::cursorPosition() {
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(textBuffer, &iter, textCursor);
return gtk_text_iter_get_offset(&iter);
}
bool pHexEdit::keyPress(unsigned scancode) {
if(!hexEdit.onRead) return false;
unsigned position = cursorPosition();
unsigned lineWidth = 10 + (hexEdit.state.columns * 3) + 1 + hexEdit.state.columns + 1;
unsigned cursorY = position / lineWidth;
unsigned cursorX = position % lineWidth;
if(scancode == GDK_Home) {
setCursorPosition(cursorY * lineWidth + 10);
return true;
}
if(scancode == GDK_End) {
setCursorPosition(cursorY * lineWidth + 10 + (hexEdit.state.columns * 3 - 1));
return true;
}
if(scancode == GDK_Up) {
if(cursorY != 0) return false;
signed newOffset = hexEdit.state.offset - hexEdit.state.columns;
if(newOffset >= 0) {
hexEdit.setOffset(newOffset);
update();
}
return true;
}
if(scancode == GDK_Down) {
if(cursorY != hexEdit.state.rows - 1) return false;
signed newOffset = hexEdit.state.offset + hexEdit.state.columns;
if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) {
hexEdit.setOffset(newOffset);
update();
}
return true;
}
if(scancode == GDK_Page_Up) {
signed newOffset = hexEdit.state.offset - hexEdit.state.columns * hexEdit.state.rows;
if(newOffset >= 0) {
hexEdit.setOffset(newOffset);
} else {
hexEdit.setOffset(0);
}
update();
return true;
}
if(scancode == GDK_Page_Down) {
signed newOffset = hexEdit.state.offset + hexEdit.state.columns * hexEdit.state.rows;
for(unsigned n = 0; n < hexEdit.state.rows; n++) {
if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) {
hexEdit.setOffset(newOffset);
update();
break;
}
newOffset -= hexEdit.state.columns;
}
return true;
}
//convert scancode to hex nibble
if(scancode >= '0' && scancode <= '9') scancode = scancode - '0';
else if(scancode >= 'A' && scancode <= 'F') scancode = scancode - 'A' + 10;
else if(scancode >= 'a' && scancode <= 'f') scancode = scancode - 'a' + 10;
else return false; //not a valid hex value
if(cursorX >= 10) {
//not on an offset
cursorX -= 10;
if((cursorX % 3) != 2) {
//not on a space
bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low
cursorX /= 3;
if(cursorX < hexEdit.state.columns) {
//not in ANSI region
unsigned offset = hexEdit.state.offset + (cursorY * hexEdit.state.columns + cursorX);
if(offset >= hexEdit.state.length) return false; //do not edit past end of data
uint8_t data = hexEdit.onRead(offset);
//write modified value
if(cursorNibble == 1) {
data = (data & 0xf0) | (scancode << 0);
} else {
data = (data & 0x0f) | (scancode << 4);
}
if(hexEdit.onWrite) hexEdit.onWrite(offset, data);
//auto-advance cursor to next nibble/byte
position++;
if(cursorNibble && cursorX != hexEdit.state.columns - 1) position++;
setCursorPosition(position);
//refresh output to reflect modified data
update();
}
}
}
return true;
}
void pHexEdit::scroll(unsigned position) {
unsigned rows = hexEdit.state.length / hexEdit.state.columns;
if(position >= rows) position = rows - 1;
hexEdit.setOffset(position * hexEdit.state.columns);
}
void pHexEdit::setCursorPosition(unsigned position) {
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(textBuffer, &iter, textCursor);
//GTK+ will throw many errors to the terminal if you set iterator past end of buffer
GtkTextIter endIter;
gtk_text_buffer_get_end_iter(textBuffer, &iter);
unsigned endPosition = gtk_text_iter_get_offset(&iter);
gtk_text_iter_set_offset(&iter, min(position, endPosition));
gtk_text_buffer_place_cursor(textBuffer, &iter);
}
void pHexEdit::setScroll() {
unsigned rows = hexEdit.state.length / hexEdit.state.columns;
if(rows) rows--;
if(rows) {
gtk_range_set_range(GTK_RANGE(scrollBar), 0, rows);
gtk_widget_set_sensitive(scrollBar, true);
} else {
gtk_widget_set_sensitive(scrollBar, false);
}
}
void pHexEdit::updateScroll() {
unsigned row = hexEdit.state.offset / hexEdit.state.columns;
gtk_range_set_value(GTK_RANGE(scrollBar), row);
}

View File

@@ -0,0 +1,24 @@
static void HorizontalSlider_change(HorizontalSlider *self) {
if(self->state.position == self->position()) return;
self->state.position = self->position();
if(self->onChange) self->onChange();
}
unsigned pHorizontalSlider::position() {
return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget));
}
void pHorizontalSlider::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
}
void pHorizontalSlider::setPosition(unsigned position) {
gtk_range_set_value(GTK_RANGE(gtkWidget), position);
}
void pHorizontalSlider::constructor() {
gtkWidget = gtk_hscale_new_with_range(0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)&horizontalSlider);
}

View File

@@ -0,0 +1,8 @@
void pLabel::setText(const string &text) {
gtk_label_set_text(GTK_LABEL(gtkWidget), text);
}
void pLabel::constructor() {
gtkWidget = gtk_label_new("");
gtk_misc_set_alignment(GTK_MISC(gtkWidget), 0.0, 0.5);
}

View File

@@ -0,0 +1,27 @@
static void LineEdit_activate(LineEdit *self) {
if(self->onActivate) self->onActivate();
}
static void LineEdit_change(LineEdit *self) {
if(self->p.locked == false && self->onChange) self->onChange();
}
void pLineEdit::setEditable(bool editable) {
gtk_entry_set_editable(GTK_ENTRY(gtkWidget), editable);
}
void pLineEdit::setText(const string &text) {
locked = true;
gtk_entry_set_text(GTK_ENTRY(gtkWidget), text);
locked = false;
}
string pLineEdit::text() {
return gtk_entry_get_text(GTK_ENTRY(gtkWidget));
}
void pLineEdit::constructor() {
gtkWidget = gtk_entry_new();
g_signal_connect_swapped(G_OBJECT(gtkWidget), "activate", G_CALLBACK(LineEdit_activate), (gpointer)&lineEdit);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "changed", G_CALLBACK(LineEdit_change), (gpointer)&lineEdit);
}

View File

@@ -0,0 +1,211 @@
static void ListView_activate(ListView *self) {
if(self->onActivate) self->onActivate();
}
static void ListView_change(ListView *self) {
if(self->state.selected == false || self->state.selection != self->selection()) {
self->state.selected = true;
self->state.selection = self->selection();
if(self->onChange) self->onChange();
}
}
static void ListView_tick(GtkCellRendererToggle *cell, gchar *path_string, ListView *self) {
unsigned row = decimal(path_string);
self->setChecked(row, !self->checked(row));
if(self->onTick) self->onTick(row);
}
void pListView::append(const lstring &text) {
GtkTreeIter iter;
gtk_list_store_append(store, &iter);
foreach(item, text, n) gtk_list_store_set(store, &iter, 1 + n, (const char*)item, -1);
}
void pListView::autoSizeColumns() {
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(subWidget));
}
bool pListView::checked(unsigned row) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreePath *path = gtk_tree_path_new_from_string(string(row));
GtkTreeIter iter;
bool state;
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, 0, &state, -1);
gtk_tree_path_free(path);
return state;
}
void pListView::modify(unsigned row, const lstring &text) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
for(unsigned i = 0; i <= row; i++) {
if(i == 0) gtk_tree_model_get_iter_first(model, &iter);
else gtk_tree_model_iter_next(model, &iter);
}
foreach(item, text, n) gtk_list_store_set(store, &iter, 1 + n, (const char*)item, -1);
}
void pListView::reset() {
listView.state.selected = false;
listView.state.selection = 0;
gtk_list_store_clear(GTK_LIST_STORE(store));
gtk_tree_view_set_model(GTK_TREE_VIEW(subWidget), GTK_TREE_MODEL(store));
//reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListView is now empty
gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(gtkWidget), 0);
gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(gtkWidget), 0);
}
bool pListView::selected() {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter) == false) return false;
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return true;
for(unsigned n = 1;; n++) {
if(gtk_tree_model_iter_next(model, &iter) == false) return false;
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return true;
}
return false;
}
unsigned pListView::selection() {
if(selected() == false) return listView.state.selection;
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter) == false) return 0;
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return 0;
for(unsigned n = 1;; n++) {
if(gtk_tree_model_iter_next(model, &iter) == false) return 0;
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return n;
}
return 0;
}
void pListView::setCheckable(bool checkable) {
gtk_tree_view_column_set_visible(column[0].column, checkable);
}
void pListView::setChecked(unsigned row, bool checked) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
GtkTreePath *path = gtk_tree_path_new_from_string(string(row));
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, path);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1);
gtk_tree_path_free(path);
}
void pListView::setHeaderText(const lstring &text) {
create();
}
void pListView::setHeaderVisible(bool visible) {
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(subWidget), visible);
}
void pListView::setSelected(bool selected) {
if(selected == false) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
gtk_tree_selection_unselect_all(selection);
} else {
setSelection(listView.state.selection);
}
}
void pListView::setSelection(unsigned row) {
signed current = -1;
if(selected()) current = selection();
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
gtk_tree_selection_unselect_all(selection);
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter) == false) return;
if(row == 0) {
gtk_tree_selection_select_iter(selection, &iter);
return;
}
for(unsigned n = 1;; n++) {
if(gtk_tree_model_iter_next(model, &iter) == false) return;
if(row == n) {
gtk_tree_selection_select_iter(selection, &iter);
return;
}
}
}
void pListView::constructor() {
gtkWidget = 0;
subWidget = 0;
create();
}
void pListView::create() {
if(subWidget) gtk_widget_destroy(subWidget);
if(gtkWidget) gtk_widget_destroy(gtkWidget);
gtkWidget = gtk_scrolled_window_new(0, 0);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
lstring headerText;
headerText.append(""); //checkbox column
foreach(headerItem, listView.state.headerText) headerText.append(headerItem);
if(headerText.size() == 1) headerText.append("");
GType *v = (GType*)malloc(headerText.size() * sizeof(GType));
foreach(header, headerText, n) v[n] = (n == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING);
store = gtk_list_store_newv(headerText.size(), v);
free(v);
subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
g_object_unref(G_OBJECT(store));
foreach(header, headerText, n) {
if(n == 0) {
column[n].renderer = gtk_cell_renderer_toggle_new();
column[n].column = gtk_tree_view_column_new_with_attributes("", column[n].renderer, "active", n, (void*)0);
gtk_tree_view_column_set_resizable(column[n].column, false);
gtk_tree_view_column_set_visible(column[n].column, false);
g_signal_connect(column[n].renderer, "toggled", G_CALLBACK(ListView_tick), (gpointer)&listView);
} else {
column[n].renderer = gtk_cell_renderer_text_new();
column[n].column = gtk_tree_view_column_new_with_attributes("", column[n].renderer, "text", n, (void*)0);
gtk_tree_view_column_set_resizable(column[n].column, true);
}
column[n].label = gtk_label_new(header);
gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(column[n].column), column[n].label);
gtk_tree_view_append_column(GTK_TREE_VIEW(subWidget), column[n].column);
gtk_widget_show(column[n].label);
}
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(subWidget), headerText.size() >= 3); //two or more columns + checkbox column
gtk_tree_view_set_search_column(GTK_TREE_VIEW(subWidget), 1);
g_signal_connect_swapped(G_OBJECT(subWidget), "cursor-changed", G_CALLBACK(ListView_change), (gpointer)&listView);
g_signal_connect_swapped(G_OBJECT(subWidget), "row-activated", G_CALLBACK(ListView_activate), (gpointer)&listView);
setHeaderVisible(listView.state.headerVisible);
setCheckable(listView.state.checkable);
foreach(text, listView.state.text) append(text);
foreach(checked, listView.state.checked, n) setChecked(n, checked);
if(listView.state.selected) setSelection(listView.state.selection);
autoSizeColumns();
gtk_widget_show(subWidget);
}
void pListView::setFocused() {
gtk_widget_grab_focus(subWidget);
}
void pListView::setFont(Font &font) {
pWidget::setFont(font);
for(unsigned n = 0; n < 1 + listView.state.headerText.size(); n++) {
gtk_widget_modify_font(column[n].label, font.p.gtkFont);
}
}

View File

@@ -0,0 +1,8 @@
void pProgressBar::setPosition(unsigned position) {
position = position <= 100 ? position : 0;
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkWidget), (double)position / 100.0);
}
void pProgressBar::constructor() {
gtkWidget = gtk_progress_bar_new();
}

View File

@@ -0,0 +1,32 @@
static void RadioBox_tick(RadioBox *self) {
if(self->p.locked == false && self->checked() && self->onTick) self->onTick();
}
bool pRadioBox::checked() {
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget));
}
void pRadioBox::setChecked() {
locked = true;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), true);
locked = false;
}
void pRadioBox::setGroup(const reference_array<RadioBox&> &group) {
foreach(item, group, n) {
if(n == 0) continue;
GSList *currentGroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(group[0].p.gtkWidget));
if(currentGroup != gtk_radio_button_get_group(GTK_RADIO_BUTTON(gtkWidget))) {
gtk_radio_button_set_group(GTK_RADIO_BUTTON(gtkWidget), currentGroup);
}
}
}
void pRadioBox::setText(const string &text) {
gtk_button_set_label(GTK_BUTTON(gtkWidget), text);
}
void pRadioBox::constructor() {
gtkWidget = gtk_radio_button_new_with_label(0, "");
g_signal_connect_swapped(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)&radioBox);
}

View File

@@ -0,0 +1,48 @@
static void TextEdit_change(TextEdit *self) {
if(self->p.locked == false && self->onChange) self->onChange();
}
void pTextEdit::setCursorPosition(unsigned position) {
GtkTextMark *mark = gtk_text_buffer_get_mark(textBuffer, "insert");
GtkTextIter iter;
gtk_text_buffer_get_end_iter(textBuffer, &iter);
gtk_text_iter_set_offset(&iter, min(position, gtk_text_iter_get_offset(&iter)));
gtk_text_buffer_place_cursor(textBuffer, &iter);
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark);
}
void pTextEdit::setEditable(bool editable) {
gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), editable);
}
void pTextEdit::setText(const string &text) {
locked = true;
gtk_text_buffer_set_text(textBuffer, text, -1);
locked = false;
}
void pTextEdit::setWordWrap(bool wordWrap) {
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
}
string pTextEdit::text() {
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(textBuffer, &start);
gtk_text_buffer_get_end_iter(textBuffer, &end);
char *temp = gtk_text_buffer_get_text(textBuffer, &start, &end, true);
string text = temp;
g_free(temp);
return text;
}
void pTextEdit::constructor() {
gtkWidget = gtk_scrolled_window_new(0, 0);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkWidget), GTK_SHADOW_ETCHED_IN);
subWidget = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), GTK_WRAP_WORD_CHAR);
gtk_container_add(GTK_CONTAINER(gtkWidget), subWidget);
textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(subWidget));
g_signal_connect_swapped(G_OBJECT(textBuffer), "changed", G_CALLBACK(TextEdit_change), (gpointer)&textEdit);
gtk_widget_show(subWidget);
}

View File

@@ -0,0 +1,24 @@
static void VerticalSlider_change(VerticalSlider *self) {
if(self->state.position == self->position()) return;
self->state.position = self->position();
if(self->onChange) self->onChange();
}
unsigned pVerticalSlider::position() {
return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget));
}
void pVerticalSlider::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
}
void pVerticalSlider::setPosition(unsigned position) {
gtk_range_set_value(GTK_RANGE(gtkWidget), position);
}
void pVerticalSlider::constructor() {
gtkWidget = gtk_vscale_new_with_range(0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)&verticalSlider);
}

View File

@@ -0,0 +1,15 @@
uintptr_t pViewport::handle() {
return GDK_WINDOW_XID(gtkWidget->window);
}
void pViewport::constructor() {
gtkWidget = gtk_drawing_area_new();
//gtk_widget_set_double_buffered(gtkWidget, false);
GdkColor color;
color.pixel = 0;
color.red = 0;
color.green = 0;
color.blue = 0;
gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, &color);
}

View File

@@ -0,0 +1,40 @@
static void Widget_setFont(GtkWidget *widget, gpointer font) {
if(font == 0) return;
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {
gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Widget_setFont, font);
}
}
bool pWidget::enabled() {
return gtk_widget_get_sensitive(gtkWidget);
}
void pWidget::setEnabled(bool enabled) {
gtk_widget_set_sensitive(gtkWidget, enabled);
}
void pWidget::setFocused() {
gtk_widget_grab_focus(gtkWidget);
}
void pWidget::setFont(Font &font) {
Widget_setFont(gtkWidget, font.p.gtkFont);
}
void pWidget::setGeometry(const Geometry &geometry) {
if(parentWindow) gtk_fixed_move(GTK_FIXED(parentWindow->formContainer), gtkWidget, geometry.x, geometry.y);
unsigned width = (signed)geometry.width <= 0 ? 1U : geometry.width;
unsigned height = (signed)geometry.height <= 0 ? 1U : geometry.height;
gtk_widget_set_size_request(gtkWidget, width, height);
}
void pWidget::setVisible(bool visible) {
if(widget.state.abstract) visible = false;
gtk_widget_set_visible(gtkWidget, visible);
}
void pWidget::constructor() {
parentWindow = 0;
if(widget.state.abstract) gtkWidget = gtk_label_new("");
}

View File

@@ -1,137 +1,237 @@
static void Action_setFont(GtkWidget *widget, gpointer font);
static void Widget_setFont(GtkWidget *widget, gpointer font);
static gint Window_close(Window *window) {
if(window->onClose) {
if(window->onClose()) window->setVisible(false);
return true;
}
if(window->onClose) window->onClose();
window->setVisible(false);
return true;
}
void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
window->x = x;
window->y = y;
window->width = width;
window->height = height;
static gboolean Window_configure(Window *window) {
if(gtk_widget_get_realized(window->p.widget) == false) return false;
object->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_move(GTK_WINDOW(object->widget), x, y);
//update geometry settings
Display *display = XOpenDisplay(0);
XWindowAttributes attributes, parentAttributes;
XGetWindowAttributes(display, GDK_WINDOW_XID(window->p.widget->window), &attributes);
X11Window rootWindow, parentWindow, *childWindow = 0;
unsigned int childCount;
XQueryTree(display, GDK_WINDOW_XID(window->p.widget->window), &rootWindow, &parentWindow, &childWindow, &childCount);
XGetWindowAttributes(display, parentWindow, &parentAttributes);
if(childWindow) XFree(childWindow);
XCloseDisplay(display);
gtk_window_set_title(GTK_WINDOW(object->widget), text);
gtk_window_set_resizable(GTK_WINDOW(object->widget), false);
gtk_widget_set_app_paintable(object->widget, true);
settings.frameGeometryX = attributes.x;
settings.frameGeometryY = attributes.y;
settings.frameGeometryWidth = parentAttributes.width - attributes.width;
settings.frameGeometryHeight = parentAttributes.height - attributes.height;
g_signal_connect_swapped(G_OBJECT(object->widget), "delete_event", G_CALLBACK(Window_close), (gpointer)this);
GtkAllocation menuAllocation, statusAllocation;
gtk_widget_get_allocation(window->p.menu, &menuAllocation);
gtk_widget_get_allocation(window->p.status, &statusAllocation);
object->menuContainer = gtk_vbox_new(false, 0);
gtk_container_add(GTK_CONTAINER(object->widget), object->menuContainer);
gtk_widget_show(object->menuContainer);
if(menuAllocation.height > 1) settings.menuGeometryHeight = menuAllocation.height;
if(statusAllocation.height > 1) settings.statusGeometryHeight = statusAllocation.height;
object->menu = gtk_menu_bar_new();
gtk_box_pack_start(GTK_BOX(object->menuContainer), object->menu, false, false, 0);
//calculate current window position
signed eventX = parentAttributes.x + attributes.x;
signed eventY = parentAttributes.y + attributes.y + window->p.menuHeight();
unsigned eventWidth = attributes.width;
unsigned eventHeight = attributes.height - window->p.menuHeight() - window->p.statusHeight();
object->formContainer = gtk_fixed_new();
gtk_widget_set_size_request(object->formContainer, width, height);
gtk_box_pack_start(GTK_BOX(object->menuContainer), object->formContainer, true, true, 0);
gtk_widget_show(object->formContainer);
//move
if(window->p.locked == false && window->state.fullScreen == false) {
if(window->state.geometry.x != eventX || window->state.geometry.y != eventY) {
window->state.geometry.x = eventX;
window->state.geometry.y = eventY;
}
}
object->statusContainer = gtk_event_box_new();
object->status = gtk_statusbar_new();
gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(object->status), false);
gtk_container_add(GTK_CONTAINER(object->statusContainer), object->status);
gtk_box_pack_start(GTK_BOX(object->menuContainer), object->statusContainer, false, false, 0);
gtk_widget_show(object->statusContainer);
if(window->onMove) window->onMove();
gtk_widget_realize(object->widget);
//size
if(window->p.locked == false && window->state.fullScreen == false) {
if(window->state.geometry.width != eventWidth || window->state.geometry.height != eventHeight) {
window->state.geometry.width = eventWidth;
window->state.geometry.height = eventHeight;
}
}
foreach(layout, window->state.layout) {
Geometry geometry = window->geometry();
geometry.x = geometry.y = 0;
layout.setGeometry(geometry);
}
if(window->onSize) window->onSize();
return false;
}
bool Window::focused() {
return gtk_window_is_active(GTK_WINDOW(object->widget));
void pWindow::append(Layout &layout) {
layout.setParent(window);
Geometry geometry = this->geometry();
geometry.x = geometry.y = 0;
layout.setGeometry(geometry);
}
void Window::setFocused() {
gtk_window_present(GTK_WINDOW(object->widget));
void pWindow::append(Menu &subMenu) {
if(window.state.menuFont) subMenu.p.setFont(*window.state.menuFont);
gtk_menu_bar_append(menu, subMenu.p.widget);
gtk_widget_show(subMenu.p.widget);
}
Geometry Window::geometry() {
gint x, y, width, height;
gtk_window_get_position(GTK_WINDOW(object->widget), &x, &y);
gtk_widget_get_size_request(object->formContainer, &width, &height);
return Geometry(x, y, width, height);
void pWindow::append(Widget &widget) {
widget.p.parentWindow = this;
if(!widget.state.font && window.state.widgetFont) {
widget.setFont(*window.state.widgetFont);
}
gtk_fixed_put(GTK_FIXED(formContainer), widget.p.gtkWidget, 0, 0);
widget.setVisible();
}
void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
gtk_window_move(GTK_WINDOW(object->widget), window->x = x, window->y = y);
gtk_widget_set_size_request(object->formContainer, window->width = width, window->height = height);
Geometry pWindow::frameMargin() {
if(window.state.fullScreen) return { 0, menuHeight(), 0, menuHeight() + statusHeight() };
return {
settings.frameGeometryX,
settings.frameGeometryY + menuHeight(),
settings.frameGeometryWidth,
settings.frameGeometryHeight + menuHeight() + statusHeight()
};
}
void Window::setDefaultFont(Font &font) {
window->defaultFont = &font;
bool pWindow::focused() {
return gtk_window_is_active(GTK_WINDOW(widget));
}
void Window::setFont(Font &font) {
Widget_setFont(object->status, font.font->font);
Geometry pWindow::geometry() {
if(window.state.fullScreen == true) {
return { 0, menuHeight(), OS::desktopGeometry().width, OS::desktopGeometry().height - menuHeight() - statusHeight() };
};
return window.state.geometry;
}
void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) {
void pWindow::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) {
GdkColor color;
color.pixel = (red << 16) | (green << 8) | (blue << 0);
color.red = (red << 8) | (red << 0);
color.green = (green << 8) | (green << 0);
color.blue = (blue << 8) | (blue << 0);
gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color);
gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &color);
}
void Window::setTitle(const string &text) {
gtk_window_set_title(GTK_WINDOW(object->widget), text);
void pWindow::setFocused() {
gtk_window_present(GTK_WINDOW(widget));
}
void Window::setStatusText(const string &text) {
gtk_statusbar_pop(GTK_STATUSBAR(object->status), 1);
gtk_statusbar_push(GTK_STATUSBAR(object->status), 1, text);
}
void Window::setMenuVisible(bool visible) {
gtk_widget_set_visible(object->menu, visible);
}
void Window::setStatusVisible(bool visible) {
gtk_widget_set_visible(object->status, visible);
}
bool Window::fullscreen() {
return window->isFullscreen;
}
void Window::setFullscreen(bool fullscreen) {
window->isFullscreen = fullscreen;
if(fullscreen == true) {
gtk_window_fullscreen(GTK_WINDOW(object->widget));
gtk_window_set_decorated(GTK_WINDOW(object->widget), false);
gtk_widget_set_size_request(object->widget, gdk_screen_width(), gdk_screen_height());
void pWindow::setFullScreen(bool fullScreen) {
if(fullScreen == false) {
gtk_window_unfullscreen(GTK_WINDOW(widget));
gtk_window_set_resizable(GTK_WINDOW(widget), window.state.resizable);
gtk_window_set_decorated(GTK_WINDOW(widget), true);
locked = true;
for(unsigned n = 0; n < 4; n++) {
setGeometry(window.state.geometry);
gtk_widget_set_size_request(widget, -1, -1);
OS::processEvents();
usleep(2000);
}
locked = false;
} else {
gtk_widget_set_size_request(object->widget, -1, -1);
gtk_window_set_decorated(GTK_WINDOW(object->widget), true);
gtk_window_unfullscreen(GTK_WINDOW(object->widget));
//at this point, GTK+ has not updated window geometry
//this causes Window::geometry() calls to return incorrect info
//thus, wait until the geometry has changed before continuing
Geometry geom;
time_t startTime = time(0);
do {
OS::run();
Geometry geom = geometry();
if(startTime - time(0) > 3) break; //prevent application from freezing
} while(geom.x == 0 && geom.y == 0 && geom.width == gdk_screen_width() && geom.height == gdk_screen_height());
gtk_window_fullscreen(GTK_WINDOW(widget));
gtk_window_set_decorated(GTK_WINDOW(widget), false);
gtk_widget_set_size_request(widget, OS::desktopGeometry().width, OS::desktopGeometry().height);
gtk_window_set_resizable(GTK_WINDOW(widget), false);
}
}
Window::Window() {
window = new Window::Data;
window->defaultFont = 0;
window->isFullscreen = false;
window->x = 0;
window->y = 0;
window->width = 0;
window->height = 0;
void pWindow::setGeometry(const Geometry &geometry) {
Geometry margin = frameMargin();
gtk_window_move(GTK_WINDOW(widget), geometry.x - margin.x, geometry.y - margin.y);
gtk_window_resize(GTK_WINDOW(widget), 1, 1);
gtk_widget_set_size_request(formContainer, geometry.width, geometry.height);
foreach(layout, window.state.layout) {
Geometry geometry = this->geometry();
geometry.x = geometry.y = 0;
layout.setGeometry(geometry);
}
}
void pWindow::setMenuFont(Font &font) {
foreach(item, window.state.menu) item.p.setFont(font);
}
void pWindow::setMenuVisible(bool visible) {
gtk_widget_set_visible(menu, visible);
}
void pWindow::setResizable(bool resizable) {
gtk_window_set_resizable(GTK_WINDOW(widget), resizable);
gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), resizable);
}
void pWindow::setStatusFont(Font &font) {
Widget_setFont(status, (gpointer)font.p.gtkFont);
}
void pWindow::setStatusText(const string &text) {
gtk_statusbar_pop(GTK_STATUSBAR(status), 1);
gtk_statusbar_push(GTK_STATUSBAR(status), 1, text);
}
void pWindow::setStatusVisible(bool visible) {
gtk_widget_set_visible(status, visible);
}
void pWindow::setTitle(const string &text) {
gtk_window_set_title(GTK_WINDOW(widget), text);
}
void pWindow::setVisible(bool visible) {
gtk_widget_set_visible(widget, visible);
}
void pWindow::setWidgetFont(Font &font) {
foreach(item, window.state.widget) {
if(!item.state.font) item.setFont(font);
}
}
void pWindow::constructor() {
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_resizable(GTK_WINDOW(widget), true);
gtk_widget_set_app_paintable(widget, true);
gtk_widget_add_events(widget, GDK_CONFIGURE);
menuContainer = gtk_vbox_new(false, 0);
gtk_container_add(GTK_CONTAINER(widget), menuContainer);
gtk_widget_show(menuContainer);
menu = gtk_menu_bar_new();
gtk_box_pack_start(GTK_BOX(menuContainer), menu, false, false, 0);
formContainer = gtk_fixed_new();
gtk_box_pack_start(GTK_BOX(menuContainer), formContainer, true, true, 0);
gtk_widget_show(formContainer);
statusContainer = gtk_event_box_new();
status = gtk_statusbar_new();
gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), true);
gtk_container_add(GTK_CONTAINER(statusContainer), status);
gtk_box_pack_start(GTK_BOX(menuContainer), statusContainer, false, false, 0);
gtk_widget_show(statusContainer);
setTitle("");
setGeometry(window.state.geometry);
g_signal_connect_swapped(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
g_signal_connect_swapped(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
}
unsigned pWindow::menuHeight() {
return window.state.menuVisible ? settings.menuGeometryHeight : 0;
}
unsigned pWindow::statusHeight() {
return window.state.statusVisible ? settings.statusGeometryHeight : 0;
}

View File

@@ -4,14 +4,34 @@
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0600
#define NOMINMAX
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <io.h>
#include <shlobj.h>
#elif defined(PHOENIX_QT)
#include <QApplication>
#include <QtGui>
#elif defined(PHOENIX_GTK)
#define None X11None
#define Window X11Window
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <cairo.h>
#include <gdk/gdkkeysyms.h>
#undef None
#undef Window
#elif defined(PHOENIX_REFERENCE)
#else
#error "phoenix: unrecognized target"
#endif
#include "phoenix.hpp"
using namespace nall;
#if defined(PHOENIX_WINDOWS)
#include "windows/windows.cpp"
#elif defined(PHOENIX_GTK)
#include "gtk/gtk.cpp"
#elif defined(PHOENIX_QT)
#include "qt/qt.cpp"
#endif
namespace phoenix {
#include "core/core.cpp"
}

View File

@@ -1,15 +1,13 @@
#include <nall/array.hpp>
#include <nall/config.hpp>
#include <nall/foreach.hpp>
#include <nall/function.hpp>
#include <nall/reference_array.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/vector.hpp>
#if defined(PHOENIX_WINDOWS)
#include "windows/windows.hpp"
#elif defined(PHOENIX_GTK)
#include "gtk/gtk.hpp"
#elif defined(PHOENIX_QT)
#include "qt/qt.hpp"
#endif
namespace phoenix {
#include "core/core.hpp"
}

View File

@@ -0,0 +1,44 @@
void pAction::setEnabled(bool enabled) {
if(dynamic_cast<Menu*>(&action)) {
((Menu&)action).p.qtMenu->setEnabled(enabled);
} else if(dynamic_cast<Separator*>(&action)) {
((Separator&)action).p.qtAction->setEnabled(enabled);
} else if(dynamic_cast<Item*>(&action)) {
((Item&)action).p.qtAction->setEnabled(enabled);
} else if(dynamic_cast<CheckItem*>(&action)) {
((CheckItem&)action).p.qtAction->setEnabled(enabled);
} else if(dynamic_cast<RadioItem*>(&action)) {
((RadioItem&)action).p.qtAction->setEnabled(enabled);
}
}
void pAction::setFont(Font &font) {
if(dynamic_cast<Menu*>(&action)) {
((Menu&)action).p.setFont(font);
} else if(dynamic_cast<Separator*>(&action)) {
((Separator&)action).p.qtAction->setFont(*font.p.qtFont);
} else if(dynamic_cast<Item*>(&action)) {
((Item&)action).p.qtAction->setFont(*font.p.qtFont);
} else if(dynamic_cast<CheckItem*>(&action)) {
((CheckItem&)action).p.qtAction->setFont(*font.p.qtFont);
} else if(dynamic_cast<RadioItem*>(&action)) {
((RadioItem&)action).p.qtAction->setFont(*font.p.qtFont);
}
}
void pAction::setVisible(bool visible) {
if(dynamic_cast<Menu*>(&action)) {
((Menu&)action).p.qtMenu->setVisible(visible);
} else if(dynamic_cast<Separator*>(&action)) {
((Separator&)action).p.qtAction->setVisible(visible);
} else if(dynamic_cast<Item*>(&action)) {
((Item&)action).p.qtAction->setVisible(visible);
} else if(dynamic_cast<CheckItem*>(&action)) {
((CheckItem&)action).p.qtAction->setVisible(visible);
} else if(dynamic_cast<RadioItem*>(&action)) {
((RadioItem&)action).p.qtAction->setVisible(visible);
}
}
void pAction::constructor() {
}

View File

@@ -0,0 +1,22 @@
bool pCheckItem::checked() {
return qtAction->isChecked();
}
void pCheckItem::setChecked(bool checked) {
qtAction->setChecked(checked);
}
void pCheckItem::setText(const string &text) {
qtAction->setText(QString::fromUtf8(text));
}
void pCheckItem::constructor() {
qtAction = new QAction(0);
qtAction->setCheckable(true);
connect(qtAction, SIGNAL(triggered()), SLOT(onTick()));
}
void pCheckItem::onTick() {
checkItem.state.checked = checked();
if(checkItem.onTick) checkItem.onTick();
}

View File

@@ -0,0 +1,12 @@
void pItem::setText(const string &text) {
qtAction->setText(QString::fromUtf8(text));
}
void pItem::constructor() {
qtAction = new QAction(0);
connect(qtAction, SIGNAL(triggered()), SLOT(onTick()));
}
void pItem::onTick() {
if(item.onTick) item.onTick();
}

View File

@@ -0,0 +1,26 @@
void pMenu::append(Action &action) {
if(dynamic_cast<Menu*>(&action)) {
qtMenu->addMenu(((Menu&)action).p.qtMenu);
} else if(dynamic_cast<Separator*>(&action)) {
qtMenu->addAction(((Separator&)action).p.qtAction);
} else if(dynamic_cast<Item*>(&action)) {
qtMenu->addAction(((Item&)action).p.qtAction);
} else if(dynamic_cast<CheckItem*>(&action)) {
qtMenu->addAction(((CheckItem&)action).p.qtAction);
} else if(dynamic_cast<RadioItem*>(&action)) {
qtMenu->addAction(((RadioItem&)action).p.qtAction);
}
}
void pMenu::setFont(Font &font) {
qtMenu->setFont(*font.p.qtFont);
foreach(item, menu.state.action) item.p.setFont(font);
}
void pMenu::setText(const string &text) {
qtMenu->setTitle(QString::fromUtf8(text));
}
void pMenu::constructor() {
qtMenu = new QMenu;
}

View File

@@ -0,0 +1,36 @@
bool pRadioItem::checked() {
return qtAction->isChecked();
}
void pRadioItem::setChecked() {
locked = true;
foreach(item, radioItem.state.group) {
bool checkState = item.p.qtAction == qtAction;
item.state.checked = checkState;
item.p.qtAction->setChecked(checkState);
}
locked = false;
}
void pRadioItem::setGroup(const reference_array<RadioItem&> &group) {
}
void pRadioItem::setText(const string &text) {
qtAction->setText(QString::fromUtf8(text));
}
void pRadioItem::constructor() {
qtAction = new QAction(0);
qtGroup = new QActionGroup(0);
qtAction->setCheckable(true);
qtAction->setActionGroup(qtGroup);
qtAction->setChecked(true);
connect(qtAction, SIGNAL(triggered()), SLOT(onTick()));
}
void pRadioItem::onTick() {
if(radioItem.state.checked == false) {
setChecked();
if(locked == false && radioItem.onTick) radioItem.onTick();
}
}

View File

@@ -0,0 +1,4 @@
void pSeparator::constructor() {
qtAction = new QAction(0);
qtAction->setSeparator(true);
}

View File

@@ -1,13 +0,0 @@
void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
button->setParent(parent.window->container);
button->setGeometry(x, y, width, height);
button->setText(QString::fromUtf8(text));
if(parent.window->defaultFont) button->setFont(*parent.window->defaultFont);
button->show();
button->connect(button, SIGNAL(released()), SLOT(onTick()));
}
Button::Button() {
button = new Button::Data(*this);
widget->widget = button;
}

View File

@@ -1,39 +0,0 @@
void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
canvas->image = new QImage(width, height, QImage::Format_RGB32);
canvas->image->fill(0);
canvas->setParent(parent.window->container);
canvas->setGeometry(x, y, width, height);
canvas->show();
}
void Canvas::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
delete canvas->image;
canvas->image = new QImage(width, height, QImage::Format_RGB32);
canvas->image->fill(0);
canvas->setGeometry(x, y, width, height);
canvas->update();
}
uint32_t* Canvas::buffer() {
return (uint32_t*)canvas->image->bits();
}
void Canvas::redraw() {
canvas->update();
}
Canvas::Canvas() {
canvas = new Canvas::Data(*this);
canvas->image = 0;
widget->widget = canvas;
}
Canvas::~Canvas() {
if(canvas->image) delete canvas->image;
delete canvas;
}
void Canvas::Data::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.drawImage(0, 0, *image);
}

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