mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-19 19:41:43 +02:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
64072325c4 | ||
|
017f9926fc | ||
|
c31543ea58 | ||
|
7c3aaf12b0 | ||
|
3fad0a0105 | ||
|
72a2967eeb | ||
|
a8ee35633c | ||
|
7dda70baa4 | ||
|
2c61ce2522 | ||
|
266495b475 | ||
|
133d568f76 | ||
|
b433838e9f | ||
|
a3abe8ebaa | ||
|
f88ef9e9a2 | ||
|
a136378a7b | ||
|
012cdd4b14 | ||
|
eecc085e42 |
@@ -65,6 +65,6 @@ clean:
|
|||||||
-@$(call delete,*.manifest)
|
-@$(call delete,*.manifest)
|
||||||
|
|
||||||
archive-all:
|
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:;
|
help:;
|
||||||
|
114797
bsnes/data/cheats.xml
114797
bsnes/data/cheats.xml
File diff suppressed because it is too large
Load Diff
@@ -3,83 +3,103 @@
|
|||||||
#define APU_CPP
|
#define APU_CPP
|
||||||
namespace GameBoy {
|
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"
|
#include "serialization.cpp"
|
||||||
APU apu;
|
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() {
|
void APU::power() {
|
||||||
|
create(Main, 4194304);
|
||||||
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||||
|
|
||||||
channel1.sweep_time = 0;
|
foreach(n, mmio_data) n = 0x00;
|
||||||
channel1.sweep_direction = 0;
|
sequencer_base = 0;
|
||||||
channel1.sweep_shift = 0;
|
sequencer_step = 0;
|
||||||
|
|
||||||
channel1.wave_pattern_duty = 0;
|
square1.power();
|
||||||
channel1.sound_length = 0;
|
square2.power();
|
||||||
|
wave.power();
|
||||||
|
noise.power();
|
||||||
|
master.power();
|
||||||
|
}
|
||||||
|
|
||||||
channel1.initial_envelope_volume = 0;
|
uint8 APU::mmio_read(uint16 addr) {
|
||||||
channel1.envelope_direction = 0;
|
static const uint8 table[48] = {
|
||||||
channel1.envelope_sweep = 0;
|
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;
|
if(addr == 0xff26) {
|
||||||
channel1.initialize = 0;
|
uint8 data = master.enable << 7;
|
||||||
channel1.consecutive_selection = 0;
|
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;
|
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
|
||||||
channel2.sound_length = 0;
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
channel2.initial_envelope_volume = 0;
|
void APU::mmio_write(uint16 addr, uint8 data) {
|
||||||
channel2.envelope_direction = 0;
|
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
|
||||||
channel2.envelope_sweep = 0;
|
|
||||||
|
|
||||||
channel2.frequency = 0;
|
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
|
||||||
channel2.initialize = 0;
|
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
|
||||||
channel2.consecutive_selection = 0;
|
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
|
||||||
|
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
|
||||||
channel3.off = 0;
|
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
|
||||||
|
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,27 @@
|
|||||||
struct APU : Processor, MMIO {
|
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();
|
void power();
|
||||||
|
|
||||||
|
uint8 mmio_read(uint16 addr);
|
||||||
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
|
||||||
void serialize(serializer&);
|
void serialize(serializer&);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
132
bsnes/gameboy/apu/master/master.cpp
Executable file
132
bsnes/gameboy/apu/master/master.cpp
Executable 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
|
24
bsnes/gameboy/apu/master/master.hpp
Executable file
24
bsnes/gameboy/apu/master/master.hpp
Executable 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&);
|
||||||
|
};
|
@@ -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
|
|
@@ -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
102
bsnes/gameboy/apu/noise/noise.cpp
Executable 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
|
24
bsnes/gameboy/apu/noise/noise.hpp
Executable file
24
bsnes/gameboy/apu/noise/noise.hpp
Executable 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&);
|
||||||
|
};
|
@@ -1,6 +1,15 @@
|
|||||||
#ifdef APU_CPP
|
#ifdef APU_CPP
|
||||||
|
|
||||||
void APU::serialize(serializer &s) {
|
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
|
#endif
|
||||||
|
146
bsnes/gameboy/apu/square1/square1.cpp
Executable file
146
bsnes/gameboy/apu/square1/square1.cpp
Executable 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
|
31
bsnes/gameboy/apu/square1/square1.hpp
Executable file
31
bsnes/gameboy/apu/square1/square1.hpp
Executable 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&);
|
||||||
|
};
|
97
bsnes/gameboy/apu/square2/square2.cpp
Executable file
97
bsnes/gameboy/apu/square2/square2.cpp
Executable 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
|
23
bsnes/gameboy/apu/square2/square2.hpp
Executable file
23
bsnes/gameboy/apu/square2/square2.hpp
Executable 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
103
bsnes/gameboy/apu/wave/wave.cpp
Executable 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
22
bsnes/gameboy/apu/wave/wave.hpp
Executable 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&);
|
||||||
|
};
|
@@ -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]();
|
ramdata = new uint8_t[ramsize = info.ramsize]();
|
||||||
|
system.load();
|
||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +116,19 @@ void Cartridge::ram_write(unsigned addr, uint8 data) {
|
|||||||
ramdata[addr] = 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() {
|
void Cartridge::power() {
|
||||||
|
bootrom_enable = true;
|
||||||
|
|
||||||
mbc0.power();
|
mbc0.power();
|
||||||
mbc1.power();
|
mbc1.power();
|
||||||
mbc2.power();
|
mbc2.power();
|
||||||
@@ -113,26 +137,10 @@ void Cartridge::power() {
|
|||||||
mmm01.power();
|
mmm01.power();
|
||||||
huc1.power();
|
huc1.power();
|
||||||
huc3.power();
|
huc3.power();
|
||||||
map();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cartridge::map() {
|
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
|
||||||
MMIO *mapper = 0;
|
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
|
||||||
switch(info.mapper) { default:
|
bus.mmio[0xff50] = this;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cartridge::Cartridge() {
|
Cartridge::Cartridge() {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
struct Cartridge : property<Cartridge> {
|
struct Cartridge : MMIO, property<Cartridge> {
|
||||||
#include "mbc0/mbc0.hpp"
|
#include "mbc0/mbc0.hpp"
|
||||||
#include "mbc1/mbc1.hpp"
|
#include "mbc1/mbc1.hpp"
|
||||||
#include "mbc2/mbc2.hpp"
|
#include "mbc2/mbc2.hpp"
|
||||||
@@ -41,6 +41,9 @@ struct Cartridge : property<Cartridge> {
|
|||||||
uint8_t *ramdata;
|
uint8_t *ramdata;
|
||||||
unsigned ramsize;
|
unsigned ramsize;
|
||||||
|
|
||||||
|
MMIO *mapper;
|
||||||
|
bool bootrom_enable;
|
||||||
|
|
||||||
void load(const string &xml, const uint8_t *data, unsigned size);
|
void load(const string &xml, const uint8_t *data, unsigned size);
|
||||||
void unload();
|
void unload();
|
||||||
|
|
||||||
@@ -49,8 +52,10 @@ struct Cartridge : property<Cartridge> {
|
|||||||
uint8 ram_read(unsigned addr);
|
uint8 ram_read(unsigned addr);
|
||||||
void ram_write(unsigned addr, uint8 data);
|
void ram_write(unsigned addr, uint8 data);
|
||||||
|
|
||||||
|
uint8 mmio_read(uint16 addr);
|
||||||
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
|
||||||
void power();
|
void power();
|
||||||
void map();
|
|
||||||
|
|
||||||
void serialize(serializer&);
|
void serialize(serializer&);
|
||||||
Cartridge();
|
Cartridge();
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
void Cartridge::serialize(serializer &s) {
|
void Cartridge::serialize(serializer &s) {
|
||||||
if(info.battery) s.array(ramdata, ramsize);
|
if(info.battery) s.array(ramdata, ramsize);
|
||||||
|
s.integer(bootrom_enable);
|
||||||
|
|
||||||
s.integer(mbc1.ram_enable);
|
s.integer(mbc1.ram_enable);
|
||||||
s.integer(mbc1.rom_select);
|
s.integer(mbc1.rom_select);
|
||||||
|
@@ -94,7 +94,7 @@ void CPU::interrupt_exec(uint16 pc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::power() {
|
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 = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
|
||||||
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
|
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
|
||||||
@@ -114,13 +114,7 @@ void CPU::power() {
|
|||||||
status.clock = 0;
|
status.clock = 0;
|
||||||
status.halt = false;
|
status.halt = false;
|
||||||
status.stop = false;
|
status.stop = false;
|
||||||
|
|
||||||
status.ime = 0;
|
status.ime = 0;
|
||||||
status.timer0 = 0;
|
|
||||||
status.timer1 = 0;
|
|
||||||
status.timer2 = 0;
|
|
||||||
status.timer3 = 0;
|
|
||||||
status.timer4 = 0;
|
|
||||||
|
|
||||||
status.p15 = 0;
|
status.p15 = 0;
|
||||||
status.p14 = 0;
|
status.p14 = 0;
|
||||||
|
@@ -17,13 +17,7 @@ struct CPU : Processor, MMIO {
|
|||||||
unsigned clock;
|
unsigned clock;
|
||||||
bool halt;
|
bool halt;
|
||||||
bool stop;
|
bool stop;
|
||||||
|
|
||||||
bool ime;
|
bool ime;
|
||||||
unsigned timer0;
|
|
||||||
unsigned timer1;
|
|
||||||
unsigned timer2;
|
|
||||||
unsigned timer3;
|
|
||||||
unsigned timer4;
|
|
||||||
|
|
||||||
//$ff00 JOYP
|
//$ff00 JOYP
|
||||||
bool p15;
|
bool p15;
|
||||||
|
@@ -21,13 +21,7 @@ void CPU::serialize(serializer &s) {
|
|||||||
s.integer(status.clock);
|
s.integer(status.clock);
|
||||||
s.integer(status.halt);
|
s.integer(status.halt);
|
||||||
s.integer(status.stop);
|
s.integer(status.stop);
|
||||||
|
|
||||||
s.integer(status.ime);
|
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.p15);
|
||||||
s.integer(status.p14);
|
s.integer(status.p14);
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
//4194304hz (4 * 1024 * 1024)
|
//4194304hz (4 * 1024 * 1024)
|
||||||
|
|
||||||
//70224 clocks/frame
|
//70224 clocks/frame
|
||||||
// 456 clocks/scanline
|
// 456 clocks/scanline
|
||||||
// 154 scanlines/frame
|
// 154 scanlines/frame
|
||||||
|
|
||||||
//4194304 / 4096 = 1024
|
|
||||||
//4194304 / 262144 = 16
|
|
||||||
//4194304 / 65536 = 64
|
|
||||||
//4394304 / 16384 = 256
|
|
||||||
|
|
||||||
#ifdef CPU_CPP
|
#ifdef CPU_CPP
|
||||||
|
|
||||||
#include "opcode.cpp"
|
#include "opcode.cpp"
|
||||||
@@ -17,43 +13,44 @@ void CPU::add_clocks(unsigned clocks) {
|
|||||||
scheduler.exit(Scheduler::ExitReason::StepEvent);
|
scheduler.exit(Scheduler::ExitReason::StepEvent);
|
||||||
|
|
||||||
status.clock += clocks;
|
status.clock += clocks;
|
||||||
if(status.clock >= 4 * 1024 * 1024) {
|
if(status.clock >= 4194304) {
|
||||||
status.clock -= 4 * 1024 * 1024;
|
status.clock -= 4194304;
|
||||||
cartridge.mbc3.second();
|
cartridge.mbc3.second();
|
||||||
}
|
}
|
||||||
|
|
||||||
status.timer0 += clocks;
|
//4194304 / N(hz) - 1 = mask
|
||||||
if(status.timer0 >= 16) timer_stage0();
|
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;
|
lcd.clock -= clocks;
|
||||||
if(cpu.clock >= 0) co_switch(scheduler.active_thread = lcd.thread);
|
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.timer_enable && status.timer_clock == 1) {
|
||||||
if(++status.tima == 0) {
|
if(++status.tima == 0) {
|
||||||
status.tima = status.tma;
|
status.tima = status.tma;
|
||||||
interrupt_raise(Interrupt::Timer);
|
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.timer_enable && status.timer_clock == 2) {
|
||||||
if(++status.tima == 0) {
|
if(++status.tima == 0) {
|
||||||
status.tima = status.tma;
|
status.tima = status.tma;
|
||||||
interrupt_raise(Interrupt::Timer);
|
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.timer_enable && status.timer_clock == 3) {
|
||||||
if(++status.tima == 0) {
|
if(++status.tima == 0) {
|
||||||
status.tima = status.tma;
|
status.tima = status.tma;
|
||||||
@@ -62,32 +59,24 @@ void CPU::timer_stage2() { // 16384hz
|
|||||||
}
|
}
|
||||||
|
|
||||||
status.div++;
|
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_transfer && status.serial_clock) {
|
||||||
if(--status.serial_bits == 0) {
|
if(--status.serial_bits == 0) {
|
||||||
status.serial_transfer = 0;
|
status.serial_transfer = 0;
|
||||||
interrupt_raise(Interrupt::Serial);
|
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.timer_enable && status.timer_clock == 0) {
|
||||||
if(++status.tima == 0) {
|
if(++status.tima == 0) {
|
||||||
status.tima = status.tma;
|
status.tima = status.tma;
|
||||||
interrupt_raise(Interrupt::Timer);
|
interrupt_raise(Interrupt::Timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status.timer4 -= 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
void add_clocks(unsigned clocks);
|
void add_clocks(unsigned clocks);
|
||||||
void timer_stage0();
|
void timer_262144hz();
|
||||||
void timer_stage1();
|
void timer_65536hz();
|
||||||
void timer_stage2();
|
void timer_16384hz();
|
||||||
void timer_stage3();
|
void timer_8192hz();
|
||||||
void timer_stage4();
|
void timer_4096hz();
|
||||||
|
|
||||||
//opcode.cpp
|
//opcode.cpp
|
||||||
void op_io();
|
void op_io();
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
namespace Info {
|
namespace Info {
|
||||||
static const char Name[] = "bgameboy";
|
static const char Name[] = "bgameboy";
|
||||||
static const char Version[] = "000.13";
|
static const char Version[] = "000.19";
|
||||||
static unsigned SerializerVersion = 1;
|
static unsigned SerializerVersion = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,22 +15,56 @@ namespace GameBoy {
|
|||||||
#include <nall/foreach.hpp>
|
#include <nall/foreach.hpp>
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/property.hpp>
|
#include <nall/property.hpp>
|
||||||
|
#include <nall/random.hpp>
|
||||||
#include <nall/serializer.hpp>
|
#include <nall/serializer.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/varint.hpp>
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
typedef int8_t int8;
|
typedef int8_t int8;
|
||||||
typedef int16_t int16;
|
typedef int16_t int16;
|
||||||
typedef int32_t int32;
|
typedef int32_t int32;
|
||||||
typedef int64_t int64;
|
typedef int64_t int64;
|
||||||
|
|
||||||
typedef uint8_t uint8;
|
typedef uint8_t uint8;
|
||||||
typedef uint16_t uint16;
|
typedef uint16_t uint16;
|
||||||
typedef uint32_t uint32;
|
typedef uint32_t uint32;
|
||||||
typedef uint64_t uint64;
|
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>
|
template<uint16 lo, uint16 hi>
|
||||||
alwaysinline bool within(uint16 addr) {
|
alwaysinline bool within(uint16 addr) {
|
||||||
static const uint16 mask = ~(hi ^ lo);
|
static const uint16 mask = ~(hi ^ lo);
|
||||||
|
@@ -3,7 +3,7 @@ public:
|
|||||||
virtual void joyp_write(bool p15, bool p14) {}
|
virtual void joyp_write(bool p15, bool p14) {}
|
||||||
|
|
||||||
virtual void video_refresh(const uint8_t *data) {}
|
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 void input_poll() {}
|
||||||
virtual bool input_poll(unsigned id) {}
|
virtual bool input_poll(unsigned id) {}
|
||||||
|
|
||||||
|
@@ -33,8 +33,8 @@ void LCD::add_clocks(unsigned clocks) {
|
|||||||
status.lx += clocks;
|
status.lx += clocks;
|
||||||
if(status.lx >= 456) scanline();
|
if(status.lx >= 456) scanline();
|
||||||
|
|
||||||
cpu.clock -= clocks;
|
clock += clocks;
|
||||||
if(cpu.clock <= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||||
co_switch(scheduler.active_thread = cpu.thread);
|
co_switch(scheduler.active_thread = cpu.thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,7 @@ void LCD::render_obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LCD::power() {
|
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 = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
|
||||||
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO
|
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO
|
||||||
|
@@ -50,7 +50,7 @@ void Bus::write(uint16 addr, uint8 data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Bus::power() {
|
void Bus::power() {
|
||||||
for(unsigned n = 0; n < 65536; n++) mmio[n] = &unmapped;
|
for(unsigned n = 0x0000; n <= 0xffff; n++) mmio[n] = &unmapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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_) {
|
void System::init(Interface *interface_) {
|
||||||
interface = interface_;
|
interface = interface_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::load() {
|
||||||
|
serialize_init();
|
||||||
|
}
|
||||||
|
|
||||||
void System::power() {
|
void System::power() {
|
||||||
bus.power();
|
bus.power();
|
||||||
cartridge.power();
|
cartridge.power();
|
||||||
@@ -58,11 +49,7 @@ void System::power() {
|
|||||||
lcd.power();
|
lcd.power();
|
||||||
scheduler.init();
|
scheduler.init();
|
||||||
|
|
||||||
for(unsigned n = 0x0000; n <= 0x00ff; n++) bus.mmio[n] = this;
|
|
||||||
bus.mmio[0xff50] = this;
|
|
||||||
|
|
||||||
clocks_executed = 0;
|
clocks_executed = 0;
|
||||||
serialize_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ enum class Input : unsigned {
|
|||||||
Up, Down, Left, Right, B, A, Select, Start,
|
Up, Down, Left, Right, B, A, Select, Start,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct System : MMIO {
|
struct System {
|
||||||
struct BootROM {
|
struct BootROM {
|
||||||
static const uint8 dmg[256];
|
static const uint8 dmg[256];
|
||||||
static const uint8 sgb[256];
|
static const uint8 sgb[256];
|
||||||
@@ -14,10 +14,8 @@ struct System : MMIO {
|
|||||||
void runtosave();
|
void runtosave();
|
||||||
void runthreadtosave();
|
void runthreadtosave();
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
|
||||||
|
|
||||||
void init(Interface*);
|
void init(Interface*);
|
||||||
|
void load();
|
||||||
void power();
|
void power();
|
||||||
|
|
||||||
Interface *interface;
|
Interface *interface;
|
||||||
|
@@ -5,8 +5,14 @@
|
|||||||
#include <nall/concept.hpp>
|
#include <nall/concept.hpp>
|
||||||
|
|
||||||
#undef foreach
|
#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(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)
|
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
|
#endif
|
||||||
|
32
bsnes/nall/public_cast.hpp
Executable file
32
bsnes/nall/public_cast.hpp
Executable 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
103
bsnes/nall/reference_array.hpp
Executable 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
|
@@ -25,7 +25,7 @@ namespace nall {
|
|||||||
inline string& append(unsigned int value);
|
inline string& append(unsigned int value);
|
||||||
inline string& append(double 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& replace (const char*, const char*);
|
||||||
inline string& qreplace(const char*, const char*);
|
inline string& qreplace(const char*, const char*);
|
||||||
|
@@ -65,8 +65,11 @@ intmax_t integer(const char *str) {
|
|||||||
intmax_t result = 0;
|
intmax_t result = 0;
|
||||||
bool negate = false;
|
bool negate = false;
|
||||||
|
|
||||||
//check for negation
|
//check for sign
|
||||||
if(*str == '-') {
|
if(*str == '+') {
|
||||||
|
negate = false;
|
||||||
|
str++;
|
||||||
|
} else if(*str == '-') {
|
||||||
negate = true;
|
negate = true;
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
|
@@ -95,7 +95,7 @@ string::~string() {
|
|||||||
if(data) free(data);
|
if(data) free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool string::readfile(const char *filename) {
|
bool string::readfile(const string &filename) {
|
||||||
assign("");
|
assign("");
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
|
@@ -50,10 +50,10 @@ string integer(intmax_t value) {
|
|||||||
result[x] = buffer[y];
|
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;
|
bool negative = value < 0;
|
||||||
if(negative) value = abs(value);
|
if(negative) value = abs(value);
|
||||||
|
|
||||||
@@ -68,6 +68,7 @@ template<unsigned length> string linteger(intmax_t value) {
|
|||||||
buffer[size++] = negative ? '-' : '+';
|
buffer[size++] = negative ? '-' : '+';
|
||||||
buffer[size] = 0;
|
buffer[size] = 0;
|
||||||
|
|
||||||
|
unsigned length = (length_ == 0 ? size : length_);
|
||||||
char result[length + 1];
|
char result[length + 1];
|
||||||
memset(result, ' ', length);
|
memset(result, ' ', length);
|
||||||
result[length] = 0;
|
result[length] = 0;
|
||||||
@@ -76,10 +77,10 @@ template<unsigned length> string linteger(intmax_t value) {
|
|||||||
result[x] = buffer[y];
|
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;
|
bool negative = value < 0;
|
||||||
if(negative) value = abs(value);
|
if(negative) value = abs(value);
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ template<unsigned length> string rinteger(intmax_t value) {
|
|||||||
buffer[size++] = negative ? '-' : '+';
|
buffer[size++] = negative ? '-' : '+';
|
||||||
buffer[size] = 0;
|
buffer[size] = 0;
|
||||||
|
|
||||||
|
unsigned length = (length_ == 0 ? size : length_);
|
||||||
char result[length + 1];
|
char result[length + 1];
|
||||||
memset(result, ' ', length);
|
memset(result, ' ', length);
|
||||||
result[length] = 0;
|
result[length] = 0;
|
||||||
@@ -102,7 +104,7 @@ template<unsigned length> string rinteger(intmax_t value) {
|
|||||||
result[x] = buffer[y];
|
result[x] = buffer[y];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return (const char*)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
string decimal(uintmax_t value) {
|
string decimal(uintmax_t value) {
|
||||||
@@ -124,10 +126,10 @@ string decimal(uintmax_t value) {
|
|||||||
result[x] = buffer[y];
|
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];
|
char buffer[64];
|
||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
|
|
||||||
@@ -138,6 +140,7 @@ template<unsigned length> string ldecimal(uintmax_t value) {
|
|||||||
} while(value);
|
} while(value);
|
||||||
buffer[size] = 0;
|
buffer[size] = 0;
|
||||||
|
|
||||||
|
unsigned length = (length_ == 0 ? size : length_);
|
||||||
char result[length + 1];
|
char result[length + 1];
|
||||||
memset(result, ' ', length);
|
memset(result, ' ', length);
|
||||||
result[length] = 0;
|
result[length] = 0;
|
||||||
@@ -146,10 +149,10 @@ template<unsigned length> string ldecimal(uintmax_t value) {
|
|||||||
result[x] = buffer[y];
|
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];
|
char buffer[64];
|
||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
|
|
||||||
@@ -160,6 +163,7 @@ template<unsigned length> string rdecimal(uintmax_t value) {
|
|||||||
} while(value);
|
} while(value);
|
||||||
buffer[size] = 0;
|
buffer[size] = 0;
|
||||||
|
|
||||||
|
unsigned length = (length_ == 0 ? size : length_);
|
||||||
char result[length + 1];
|
char result[length + 1];
|
||||||
memset(result, ' ', length);
|
memset(result, ' ', length);
|
||||||
result[length] = 0;
|
result[length] = 0;
|
||||||
@@ -168,53 +172,51 @@ template<unsigned length> string rdecimal(uintmax_t value) {
|
|||||||
result[x] = buffer[y];
|
result[x] = buffer[y];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return (const char*)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned length> string hex(uintmax_t value) {
|
template<unsigned length_> string hex(uintmax_t value) {
|
||||||
string output;
|
char buffer[64];
|
||||||
unsigned offset = 0;
|
unsigned size = 0;
|
||||||
|
|
||||||
//render string backwards, as we do not know its length yet
|
|
||||||
do {
|
do {
|
||||||
unsigned n = value & 15;
|
unsigned n = value & 15;
|
||||||
output[offset++] = n < 10 ? '0' + n : 'a' + n - 10;
|
buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
|
||||||
value >>= 4;
|
value >>= 4;
|
||||||
} while(value);
|
} while(value);
|
||||||
|
|
||||||
while(offset < length) output[offset++] = '0';
|
unsigned length = (length_ == 0 ? size : length_);
|
||||||
output[offset--] = 0;
|
char result[length + 1];
|
||||||
|
memset(result, '0', length);
|
||||||
|
result[length] = 0;
|
||||||
|
|
||||||
//reverse the string in-place
|
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
|
||||||
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
|
result[x] = buffer[y];
|
||||||
char temp = output[i];
|
|
||||||
output[i] = output[offset - i];
|
|
||||||
output[offset - i] = temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return (const char*)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned length> string binary(uintmax_t value) {
|
template<unsigned length_> string binary(uintmax_t value) {
|
||||||
string output;
|
char buffer[256];
|
||||||
unsigned offset = 0;
|
unsigned size = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
unsigned n = value & 1;
|
unsigned n = value & 1;
|
||||||
output[offset++] = '0' + n;
|
buffer[size++] = '0' + n;
|
||||||
value >>= 1;
|
value >>= 1;
|
||||||
} while(value);
|
} while(value);
|
||||||
|
|
||||||
while(offset < length) output[offset++] = '0';
|
unsigned length = (length_ == 0 ? size : length_);
|
||||||
output[offset--] = 0;
|
char result[length + 1];
|
||||||
|
memset(result, '0', length);
|
||||||
|
result[length] = 0;
|
||||||
|
|
||||||
for(unsigned i = 0; i < (offset + 1) >> 1; i++) {
|
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
|
||||||
char temp = output[i];
|
result[x] = buffer[y];
|
||||||
output[i] = output[offset - i];
|
|
||||||
output[offset - i] = temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return (const char*)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//using sprintf is certainly not the most ideal method to convert
|
//using sprintf is certainly not the most ideal method to convert
|
||||||
|
170
bsnes/phoenix/core/core.cpp
Executable file
170
bsnes/phoenix/core/core.cpp
Executable 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
405
bsnes/phoenix/core/core.hpp
Executable 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"
|
20
bsnes/phoenix/core/layout/fixed-layout.cpp
Executable file
20
bsnes/phoenix/core/layout/fixed-layout.cpp
Executable 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() {
|
||||||
|
}
|
15
bsnes/phoenix/core/layout/fixed-layout.hpp
Executable file
15
bsnes/phoenix/core/layout/fixed-layout.hpp
Executable 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;
|
||||||
|
};
|
88
bsnes/phoenix/core/layout/horizontal-layout.cpp
Executable file
88
bsnes/phoenix/core/layout/horizontal-layout.cpp
Executable 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;
|
||||||
|
}
|
23
bsnes/phoenix/core/layout/horizontal-layout.hpp
Executable file
23
bsnes/phoenix/core/layout/horizontal-layout.hpp
Executable 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;
|
||||||
|
};
|
88
bsnes/phoenix/core/layout/vertical-layout.cpp
Executable file
88
bsnes/phoenix/core/layout/vertical-layout.cpp
Executable 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;
|
||||||
|
}
|
23
bsnes/phoenix/core/layout/vertical-layout.hpp
Executable file
23
bsnes/phoenix/core/layout/vertical-layout.hpp
Executable 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
224
bsnes/phoenix/core/state.hpp
Executable 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;
|
||||||
|
}
|
||||||
|
};
|
22
bsnes/phoenix/gtk/action/action.cpp
Executable file
22
bsnes/phoenix/gtk/action/action.cpp
Executable 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() {
|
||||||
|
}
|
22
bsnes/phoenix/gtk/action/check-item.cpp
Executable file
22
bsnes/phoenix/gtk/action/check-item.cpp
Executable 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);
|
||||||
|
}
|
12
bsnes/phoenix/gtk/action/item.cpp
Executable file
12
bsnes/phoenix/gtk/action/item.cpp
Executable 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);
|
||||||
|
}
|
19
bsnes/phoenix/gtk/action/menu.cpp
Executable file
19
bsnes/phoenix/gtk/action/menu.cpp
Executable 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);
|
||||||
|
}
|
33
bsnes/phoenix/gtk/action/radio-item.cpp
Executable file
33
bsnes/phoenix/gtk/action/radio-item.cpp
Executable 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);
|
||||||
|
}
|
3
bsnes/phoenix/gtk/action/separator.cpp
Executable file
3
bsnes/phoenix/gtk/action/separator.cpp
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
void pSeparator::constructor() {
|
||||||
|
widget = gtk_separator_menu_item_new();
|
||||||
|
}
|
@@ -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);
|
|
||||||
}
|
|
@@ -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;
|
|
||||||
}
|
|
@@ -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;
|
|
||||||
}
|
|
@@ -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;
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -1,18 +1,22 @@
|
|||||||
bool Font::create(const string &name, unsigned size, Font::Style style) {
|
void pFont::setBold(bool bold) {
|
||||||
font->font = pango_font_description_new();
|
pango_font_description_set_weight(gtkFont, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::Font() {
|
void pFont::setFamily(const string &family) {
|
||||||
font = new Font::Data;
|
pango_font_description_set_family(gtkFont, family);
|
||||||
font->font = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::~Font() {
|
void pFont::setItalic(bool italic) {
|
||||||
if(font->font) pango_font_description_free(font->font);
|
pango_font_description_set_style(gtkFont, italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL);
|
||||||
delete font;
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
@@ -1,49 +1,136 @@
|
|||||||
#include <unistd.h>
|
#include "gtk.hpp"
|
||||||
#include <pwd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#define None X11None
|
#include "settings.cpp"
|
||||||
#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 "font.cpp"
|
#include "font.cpp"
|
||||||
#include "menu.cpp"
|
#include "message-window.cpp"
|
||||||
#include "widget.cpp"
|
|
||||||
#include "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() {
|
#include "widget/widget.cpp"
|
||||||
static bool initialized = false;
|
#include "widget/button.cpp"
|
||||||
if(initialized == true) return;
|
#include "widget/check-box.cpp"
|
||||||
initialized = true;
|
#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;
|
int argc = 1;
|
||||||
char *argv[2];
|
char *argv[2];
|
||||||
@@ -63,128 +150,3 @@ void OS::initialize() {
|
|||||||
"class \"GtkTreeView\" style \"phoenix-gtk\"\n"
|
"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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -1,289 +1,351 @@
|
|||||||
namespace phoenix {
|
struct Settings : public configuration {
|
||||||
|
unsigned frameGeometryX;
|
||||||
|
unsigned frameGeometryY;
|
||||||
|
unsigned frameGeometryWidth;
|
||||||
|
unsigned frameGeometryHeight;
|
||||||
|
unsigned menuGeometryHeight;
|
||||||
|
unsigned statusGeometryHeight;
|
||||||
|
|
||||||
struct Window;
|
void load();
|
||||||
|
void save();
|
||||||
struct Object {
|
Settings();
|
||||||
Object();
|
|
||||||
Object& operator=(const Object&) = delete;
|
|
||||||
Object(const Object&) = delete;
|
|
||||||
//private:
|
|
||||||
virtual void unused();
|
|
||||||
struct Data;
|
|
||||||
Data *object;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Geometry {
|
struct pFont;
|
||||||
unsigned x, y;
|
struct pWindow;
|
||||||
unsigned width, height;
|
struct pMenu;
|
||||||
inline Geometry() : x(0), y(0), width(0), height(0) {}
|
struct pLayout;
|
||||||
inline Geometry(unsigned x, unsigned y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
|
struct pWidget;
|
||||||
|
|
||||||
|
struct pObject {
|
||||||
|
bool locked;
|
||||||
|
|
||||||
|
pObject() {
|
||||||
|
locked = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Font : Object {
|
struct pOS : public pObject {
|
||||||
enum class Style : unsigned {
|
static Geometry availableGeometry();
|
||||||
None = 0,
|
static Geometry desktopGeometry();
|
||||||
Bold = 1,
|
static string fileLoad(Window &parent, const string &path, const lstring &filter);
|
||||||
Italic = 2,
|
static string fileSave(Window &parent, const string &path, const lstring &filter);
|
||||||
};
|
static string folderSelect(Window &parent, const string &path);
|
||||||
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();
|
|
||||||
static void main();
|
static void main();
|
||||||
|
static bool pendingEvents();
|
||||||
|
static void processEvents();
|
||||||
static void quit();
|
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();
|
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();
|
||||||
|
};
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -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;
|
|
||||||
}
|
|
@@ -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;
|
|
||||||
}
|
|
@@ -8,13 +8,12 @@ static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons but
|
|||||||
return MessageWindow::Response::Ok;
|
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;
|
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
|
||||||
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
||||||
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
||||||
|
|
||||||
GtkWidget *dialog = gtk_message_dialog_new(
|
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
|
GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", (const char*)text
|
||||||
);
|
);
|
||||||
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
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);
|
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;
|
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
|
||||||
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
||||||
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
||||||
|
|
||||||
GtkWidget *dialog = gtk_message_dialog_new(
|
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
|
GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", (const char*)text
|
||||||
);
|
);
|
||||||
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
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);
|
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;
|
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
|
||||||
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
||||||
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
||||||
|
|
||||||
GtkWidget *dialog = gtk_message_dialog_new(
|
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
|
GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", (const char*)text
|
||||||
);
|
);
|
||||||
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
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);
|
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;
|
GtkButtonsType buttonsType = GTK_BUTTONS_OK;
|
||||||
if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
if(buttons == MessageWindow::Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL;
|
||||||
if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
if(buttons == MessageWindow::Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO;
|
||||||
|
|
||||||
GtkWidget *dialog = gtk_message_dialog_new(
|
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
|
GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", (const char*)text
|
||||||
);
|
);
|
||||||
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
@@ -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;
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -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
24
bsnes/phoenix/gtk/settings.cpp
Executable 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");
|
||||||
|
}
|
@@ -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;
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -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;
|
|
||||||
}
|
|
12
bsnes/phoenix/gtk/widget/button.cpp
Executable file
12
bsnes/phoenix/gtk/widget/button.cpp
Executable 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);
|
||||||
|
}
|
22
bsnes/phoenix/gtk/widget/check-box.cpp
Executable file
22
bsnes/phoenix/gtk/widget/check-box.cpp
Executable 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);
|
||||||
|
}
|
33
bsnes/phoenix/gtk/widget/combo-box.cpp
Executable file
33
bsnes/phoenix/gtk/widget/combo-box.cpp
Executable 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);
|
||||||
|
}
|
246
bsnes/phoenix/gtk/widget/hex-edit.cpp
Executable file
246
bsnes/phoenix/gtk/widget/hex-edit.cpp
Executable 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);
|
||||||
|
}
|
24
bsnes/phoenix/gtk/widget/horizontal-slider.cpp
Executable file
24
bsnes/phoenix/gtk/widget/horizontal-slider.cpp
Executable 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);
|
||||||
|
}
|
8
bsnes/phoenix/gtk/widget/label.cpp
Executable file
8
bsnes/phoenix/gtk/widget/label.cpp
Executable 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);
|
||||||
|
}
|
27
bsnes/phoenix/gtk/widget/line-edit.cpp
Executable file
27
bsnes/phoenix/gtk/widget/line-edit.cpp
Executable 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);
|
||||||
|
}
|
211
bsnes/phoenix/gtk/widget/list-view.cpp
Executable file
211
bsnes/phoenix/gtk/widget/list-view.cpp
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
8
bsnes/phoenix/gtk/widget/progress-bar.cpp
Executable file
8
bsnes/phoenix/gtk/widget/progress-bar.cpp
Executable 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();
|
||||||
|
}
|
32
bsnes/phoenix/gtk/widget/radio-box.cpp
Executable file
32
bsnes/phoenix/gtk/widget/radio-box.cpp
Executable 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);
|
||||||
|
}
|
48
bsnes/phoenix/gtk/widget/text-edit.cpp
Executable file
48
bsnes/phoenix/gtk/widget/text-edit.cpp
Executable 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);
|
||||||
|
}
|
24
bsnes/phoenix/gtk/widget/vertical-slider.cpp
Executable file
24
bsnes/phoenix/gtk/widget/vertical-slider.cpp
Executable 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);
|
||||||
|
}
|
15
bsnes/phoenix/gtk/widget/viewport.cpp
Executable file
15
bsnes/phoenix/gtk/widget/viewport.cpp
Executable 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);
|
||||||
|
}
|
40
bsnes/phoenix/gtk/widget/widget.cpp
Executable file
40
bsnes/phoenix/gtk/widget/widget.cpp
Executable 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("");
|
||||||
|
}
|
@@ -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) {
|
static gint Window_close(Window *window) {
|
||||||
if(window->onClose) {
|
if(window->onClose) window->onClose();
|
||||||
if(window->onClose()) window->setVisible(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
window->setVisible(false);
|
window->setVisible(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) {
|
static gboolean Window_configure(Window *window) {
|
||||||
window->x = x;
|
if(gtk_widget_get_realized(window->p.widget) == false) return false;
|
||||||
window->y = y;
|
|
||||||
window->width = width;
|
|
||||||
window->height = height;
|
|
||||||
|
|
||||||
object->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
//update geometry settings
|
||||||
gtk_window_move(GTK_WINDOW(object->widget), x, y);
|
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);
|
settings.frameGeometryX = attributes.x;
|
||||||
gtk_window_set_resizable(GTK_WINDOW(object->widget), false);
|
settings.frameGeometryY = attributes.y;
|
||||||
gtk_widget_set_app_paintable(object->widget, true);
|
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);
|
if(menuAllocation.height > 1) settings.menuGeometryHeight = menuAllocation.height;
|
||||||
gtk_container_add(GTK_CONTAINER(object->widget), object->menuContainer);
|
if(statusAllocation.height > 1) settings.statusGeometryHeight = statusAllocation.height;
|
||||||
gtk_widget_show(object->menuContainer);
|
|
||||||
|
|
||||||
object->menu = gtk_menu_bar_new();
|
//calculate current window position
|
||||||
gtk_box_pack_start(GTK_BOX(object->menuContainer), object->menu, false, false, 0);
|
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();
|
//move
|
||||||
gtk_widget_set_size_request(object->formContainer, width, height);
|
if(window->p.locked == false && window->state.fullScreen == false) {
|
||||||
gtk_box_pack_start(GTK_BOX(object->menuContainer), object->formContainer, true, true, 0);
|
if(window->state.geometry.x != eventX || window->state.geometry.y != eventY) {
|
||||||
gtk_widget_show(object->formContainer);
|
window->state.geometry.x = eventX;
|
||||||
|
window->state.geometry.y = eventY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object->statusContainer = gtk_event_box_new();
|
if(window->onMove) window->onMove();
|
||||||
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);
|
|
||||||
|
|
||||||
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() {
|
void pWindow::append(Layout &layout) {
|
||||||
return gtk_window_is_active(GTK_WINDOW(object->widget));
|
layout.setParent(window);
|
||||||
|
Geometry geometry = this->geometry();
|
||||||
|
geometry.x = geometry.y = 0;
|
||||||
|
layout.setGeometry(geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setFocused() {
|
void pWindow::append(Menu &subMenu) {
|
||||||
gtk_window_present(GTK_WINDOW(object->widget));
|
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() {
|
void pWindow::append(Widget &widget) {
|
||||||
gint x, y, width, height;
|
widget.p.parentWindow = this;
|
||||||
gtk_window_get_position(GTK_WINDOW(object->widget), &x, &y);
|
if(!widget.state.font && window.state.widgetFont) {
|
||||||
gtk_widget_get_size_request(object->formContainer, &width, &height);
|
widget.setFont(*window.state.widgetFont);
|
||||||
return Geometry(x, y, width, height);
|
}
|
||||||
|
gtk_fixed_put(GTK_FIXED(formContainer), widget.p.gtkWidget, 0, 0);
|
||||||
|
widget.setVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
|
Geometry pWindow::frameMargin() {
|
||||||
gtk_window_move(GTK_WINDOW(object->widget), window->x = x, window->y = y);
|
if(window.state.fullScreen) return { 0, menuHeight(), 0, menuHeight() + statusHeight() };
|
||||||
gtk_widget_set_size_request(object->formContainer, window->width = width, window->height = height);
|
return {
|
||||||
|
settings.frameGeometryX,
|
||||||
|
settings.frameGeometryY + menuHeight(),
|
||||||
|
settings.frameGeometryWidth,
|
||||||
|
settings.frameGeometryHeight + menuHeight() + statusHeight()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setDefaultFont(Font &font) {
|
bool pWindow::focused() {
|
||||||
window->defaultFont = &font;
|
return gtk_window_is_active(GTK_WINDOW(widget));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setFont(Font &font) {
|
Geometry pWindow::geometry() {
|
||||||
Widget_setFont(object->status, font.font->font);
|
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;
|
GdkColor color;
|
||||||
color.pixel = (red << 16) | (green << 8) | (blue << 0);
|
color.pixel = (red << 16) | (green << 8) | (blue << 0);
|
||||||
color.red = (red << 8) | (red << 0);
|
color.red = (red << 8) | (red << 0);
|
||||||
color.green = (green << 8) | (green << 0);
|
color.green = (green << 8) | (green << 0);
|
||||||
color.blue = (blue << 8) | (blue << 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) {
|
void pWindow::setFocused() {
|
||||||
gtk_window_set_title(GTK_WINDOW(object->widget), text);
|
gtk_window_present(GTK_WINDOW(widget));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setStatusText(const string &text) {
|
void pWindow::setFullScreen(bool fullScreen) {
|
||||||
gtk_statusbar_pop(GTK_STATUSBAR(object->status), 1);
|
if(fullScreen == false) {
|
||||||
gtk_statusbar_push(GTK_STATUSBAR(object->status), 1, text);
|
gtk_window_unfullscreen(GTK_WINDOW(widget));
|
||||||
}
|
gtk_window_set_resizable(GTK_WINDOW(widget), window.state.resizable);
|
||||||
|
gtk_window_set_decorated(GTK_WINDOW(widget), true);
|
||||||
void Window::setMenuVisible(bool visible) {
|
locked = true;
|
||||||
gtk_widget_set_visible(object->menu, visible);
|
for(unsigned n = 0; n < 4; n++) {
|
||||||
}
|
setGeometry(window.state.geometry);
|
||||||
|
gtk_widget_set_size_request(widget, -1, -1);
|
||||||
void Window::setStatusVisible(bool visible) {
|
OS::processEvents();
|
||||||
gtk_widget_set_visible(object->status, visible);
|
usleep(2000);
|
||||||
}
|
}
|
||||||
|
locked = false;
|
||||||
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());
|
|
||||||
} else {
|
} else {
|
||||||
gtk_widget_set_size_request(object->widget, -1, -1);
|
gtk_window_fullscreen(GTK_WINDOW(widget));
|
||||||
gtk_window_set_decorated(GTK_WINDOW(object->widget), true);
|
gtk_window_set_decorated(GTK_WINDOW(widget), false);
|
||||||
gtk_window_unfullscreen(GTK_WINDOW(object->widget));
|
gtk_widget_set_size_request(widget, OS::desktopGeometry().width, OS::desktopGeometry().height);
|
||||||
|
gtk_window_set_resizable(GTK_WINDOW(widget), false);
|
||||||
//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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::Window() {
|
void pWindow::setGeometry(const Geometry &geometry) {
|
||||||
window = new Window::Data;
|
Geometry margin = frameMargin();
|
||||||
window->defaultFont = 0;
|
gtk_window_move(GTK_WINDOW(widget), geometry.x - margin.x, geometry.y - margin.y);
|
||||||
window->isFullscreen = false;
|
gtk_window_resize(GTK_WINDOW(widget), 1, 1);
|
||||||
window->x = 0;
|
gtk_widget_set_size_request(formContainer, geometry.width, geometry.height);
|
||||||
window->y = 0;
|
foreach(layout, window.state.layout) {
|
||||||
window->width = 0;
|
Geometry geometry = this->geometry();
|
||||||
window->height = 0;
|
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;
|
||||||
}
|
}
|
||||||
|
@@ -4,14 +4,34 @@
|
|||||||
#define _WIN32_WINNT 0x0501
|
#define _WIN32_WINNT 0x0501
|
||||||
#define _WIN32_IE 0x0600
|
#define _WIN32_IE 0x0600
|
||||||
#define NOMINMAX
|
#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
|
#endif
|
||||||
|
|
||||||
#include "phoenix.hpp"
|
#include "phoenix.hpp"
|
||||||
|
using namespace nall;
|
||||||
|
|
||||||
#if defined(PHOENIX_WINDOWS)
|
namespace phoenix {
|
||||||
#include "windows/windows.cpp"
|
#include "core/core.cpp"
|
||||||
#elif defined(PHOENIX_GTK)
|
}
|
||||||
#include "gtk/gtk.cpp"
|
|
||||||
#elif defined(PHOENIX_QT)
|
|
||||||
#include "qt/qt.cpp"
|
|
||||||
#endif
|
|
||||||
|
@@ -1,15 +1,13 @@
|
|||||||
#include <nall/array.hpp>
|
#include <nall/array.hpp>
|
||||||
|
#include <nall/config.hpp>
|
||||||
#include <nall/foreach.hpp>
|
#include <nall/foreach.hpp>
|
||||||
#include <nall/function.hpp>
|
#include <nall/function.hpp>
|
||||||
|
#include <nall/reference_array.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
#include <nall/utility.hpp>
|
#include <nall/utility.hpp>
|
||||||
#include <nall/vector.hpp>
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
#if defined(PHOENIX_WINDOWS)
|
namespace phoenix {
|
||||||
#include "windows/windows.hpp"
|
#include "core/core.hpp"
|
||||||
#elif defined(PHOENIX_GTK)
|
}
|
||||||
#include "gtk/gtk.hpp"
|
|
||||||
#elif defined(PHOENIX_QT)
|
|
||||||
#include "qt/qt.hpp"
|
|
||||||
#endif
|
|
||||||
|
44
bsnes/phoenix/qt/action/action.cpp
Executable file
44
bsnes/phoenix/qt/action/action.cpp
Executable 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() {
|
||||||
|
}
|
22
bsnes/phoenix/qt/action/check-item.cpp
Executable file
22
bsnes/phoenix/qt/action/check-item.cpp
Executable 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();
|
||||||
|
}
|
12
bsnes/phoenix/qt/action/item.cpp
Executable file
12
bsnes/phoenix/qt/action/item.cpp
Executable 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();
|
||||||
|
}
|
26
bsnes/phoenix/qt/action/menu.cpp
Executable file
26
bsnes/phoenix/qt/action/menu.cpp
Executable 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;
|
||||||
|
}
|
36
bsnes/phoenix/qt/action/radio-item.cpp
Executable file
36
bsnes/phoenix/qt/action/radio-item.cpp
Executable 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();
|
||||||
|
}
|
||||||
|
}
|
4
bsnes/phoenix/qt/action/separator.cpp
Executable file
4
bsnes/phoenix/qt/action/separator.cpp
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
void pSeparator::constructor() {
|
||||||
|
qtAction = new QAction(0);
|
||||||
|
qtAction->setSeparator(true);
|
||||||
|
}
|
@@ -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;
|
|
||||||
}
|
|
@@ -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
Reference in New Issue
Block a user