Xedi.Xermawan's Blog

personal-technical blog

galeri kalender 2015 2016 ( Indonesia )

with one comment

Jakarta, November 16, 2014

Beberapa galeri kalender 2015 Indonesia ( dengan pasaran jawa : pon , pahing , kliwon , legi , wage )
download :  LINK

Kalender 2015 Indonesia

Kalender 2015 Indonesia

kalender 2015 Indonesia yang lain :  LINK

dibuat dengan program

Update Oktober 2015 :

saya membuat banyak sekali kalender 2016 dengan berbagai macam type font. bisa didownload disini :
kalender_2016_VastShadow-Regular.ttf

Update Desember 2015 :

Beberapa minggu lalu saya membuat kalender Indonesia versi Android. di dalam versi android dimasukkan juga liburan dan cuti bersama 2016. Kalau tertarik bisa diinstall disini.

atau scan barcode dibawah ini :
Kalender_Indonesia_Link

Tampilan Kalender Indonesia versi Android :

Kalender_Indonesia_2016

Update Maret 2016 :

penambahan fitur kalender lunar / kalender bulan ( dipakai di kalender Jawa dan Kalender Hijriah ). Namun untuk kalender hijriah akurasi nya +/- 1 hari.

Update Desember 2016 :
Kalender Indonesia 2017 telah diupdate dengan libur nasional dan cuti bersama 2017, termasuk penambahan 3 hari libur dari SKB 3 menteri 6 desember 2016 : 1 Juni, 2 Januari sebagai cuti bersama, dan penambahan 1 hari & pergeseran libur di cuti bersama idul fitri 1438 H ( juni 2017 ).

Download Kalender Indonesia di Android disini .

atau scan barcode dibawah ini :
Kalender_Indonesia_Link

Semoga bermanfaat 🙂

my calendar maker

with one comment

walaupun tahun baru masih 1 1/2 bulan lagi, entah dapat dari mana ide-nya, saya tertarik untuk membuat kalender. Mungkin pikiran saya secara spontan kembali ke masa lalu ketika di tahun pertama kuliah dapat tugas membuat program kalender abadi he he.  Yang saya buat disini tidak jauh beda : program untuk membuat kalender, dengan input tahun dan bulan , output nya berupa image kalender. Jadi image nya bisa dipakai, atau mungkin di print 🙂 . Dan program ini dimaksudkan bisa membuat kalender tahun berapa pun, termasuk tahun 1, bulan 1. Walaupun doeloe pernah buat, saya sudah lupa gimana buatnya. sudah lama sekali. Setelah search tentang kalender di wiki , berikut beberapa hal yang perlu diketahui, untuk menghitung hari dari tahun 1 :

[1] tahun 1, bulan 1, tanggal 1 adalah hari Sabtu.

[2] dari tahun 1 sampai sekarang, ada dua 2 sistem perhitungan kalender yang pernah dipakai : Julian kalender & Gregorian kalender. Sistem Julian kalender dipakai tahun 1 sampai dengan tahun 1581 . tahun 1582 sampai sekarang pakai sistem Gregorian kalender.

[3] jumlah bulan di Julian & Gregorian kalender tidak berbeda (mulai januari :31, 28/29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 )  . Bulan Februari 29 hari jika tahun tersebut kabisat (leap year) . jadi 1 tahun bisa 365 hari atau 366 hari .

[4] di Julian kalender, sebuah tahun dikatakan kabisat, jika tahun tersebut habis dibagi 4. artinya tahun kabisat selalu berulang setiap 4 tahun.

[5] di Gregorian kalender, tahun dikatakan kabisat jika tahun tersebut habis dibagi 4, tapi jika tahun tersebut habis di bagi 100, untuk disebut tahun kabisat, tahun tersebut harus habis juga di bagi 400. jadi tahun 8 adalah kabisat, tahun 1900 bukan kabisat. pseudo-code untuk menentukan ini :  ( source here ) :

if (year is not divisible by 4) then (it is a common year)
else
if (year is not divisible by 100) then (it is a leap year)
else
if (year is not divisible by 400) then (it is a common year)
else (it is a leap year)

ternyata perhitungan tahun-kabisat yang tricky ini, sudah menimbulkan banyak bug di berbagai software  🙂

[6] di tahun 1582, saat pergantian dari sistem Julian ke Gregorian, ada pemotongan 10 hari !! di bulan Desember. Bulan Desember 1582 , berakhir tanggal 21, keesokan harinya tahun baru tanggal 1 1583 .

dengan info diatas, hari bisa dihitung dengan benar. Di program pembuat kalender ini, saya masukkan juga pasaran jawa ( Pon, Pahing, Wage, Kliwon, Legi ) :d . ini penting soalnya digunakan mbah/ibu saya untuk pergi ke pasar :-p . Hasil kalendar saya cetak di gambar berformat png. Bisa juga menggunakan png image yang sudah ada untuk dijadikan background . Bagaimana dengan hari libur ? . pengaturan hari libur juga dimasukkan dan bisa diatur di config file . program beroperasi dengan 2 type, mencetak 1 tahun penuh atau 1 bulan saja. contoh hasil program untuk mencetak 1 bulan ( tahun 1945, bulan 8 ) :

http://pngwriter.sourceforge.net/

hari Minggu otomatis berwarna merah dan Jum’at berwarna hijau. sedangkan hari-hari libur yang di-setting akan berwarna merah. default warna font dan jenis font bisa di atur di config file . penggunaan program di console sebagai berikut :

E:\test>ccm config2.ini

kalau tertarik mencoba , binary bisa didownload  di: LINK DOWNLOAD 1

dependencies code : pngwrite –> libpng–>zlib ( untuk berurusan dengan png ), dan inih (untuk parsing ini file )

main program sebagai berikut :

/*
*  console calendar maker ( ccm )
*  console app to generate calendar from year-month input, the output is png image
*  (c) 2014  edi ermawan < edi.ermawan@gmail.com >
*/

#include <iostream>
#include <stdio.h>
#include "png.hpp"
#include <pngwriter.h>
#include <sstream>
#include <string>
#include "color.h"
#include "ini.h"

using namespace std;

#define MOONINYEAR 12
#define DAYSINWEEK 7
#define DAY_SPACING "   "
#define DAY_COUNTED_SPACING "  "
#define CALOUT "calendar_out.png"

static char* day_name[DAYSINWEEK] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };
static char* month_name[MOONINYEAR] = { "January", "February", "March", "April", "May",
							        "June", "July","August","September","October",
							        "November","December"};
static char* pasaran_name[5] = { " Wage ","Kliwon "," Legi ", "Pahing", " Pon  " };

static const int day_in_month[MOONINYEAR] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

struct s_printer {
	int print_type;
	int year_target;
	int month_target;
	char* background_img;
	int font_size;
	int pasaran_font_size;
	int moon_title_font_size;
	char* font_name;
	int start_x;
	int start_y;
	int pasaran_adjust_x;
	int pasaran_adjust_y;
	int row_space;
	int col_space;
	int month_horizontal_space;
	int month_vertical_space;
	int month_footer_space;
	int default_r;
	int default_g;
	int default_b;
	int img_w;
	int img_h;
};

struct s_holiday {
	int month;
	int day;
	char* info;
};

s_printer printer;
s_holiday holidays[80];
int holiday_index = 0;

bool is_holiday(int month, int day) {
	for (int i = 0; i < holiday_index; i++) {
		if (holidays[i].month == month && holidays[i].day == day) {
			return true;
		}
	}
	return false;
}

bool is_leap_year_1(int year) {
	if ( (year % 4 == 0) )
		return true;
	return false;
}

bool is_leap_year_2( int year ) {
	if ( ((year % 4 == 0) && (year % 100)) || (year % 400 == 0) )
		return true;
	return false;
}

bool is_leap_year(int year) {
	if (year > 1581) {
		return is_leap_year_2(year);
	}
	else {
		return is_leap_year_1(year);
	}
}

int get_month_length(bool leap_year, int nth_month) {
	if (nth_month != 1) {
		return day_in_month[nth_month];
	}
	if (leap_year)
		return 29;
	return 28;
}

int get_days_until_month(bool leap_year,int n) {
	int days = 0;
	while (n--)
		days += get_month_length(leap_year, n);
	return days;
}

int get_days_in_year(int y) {
	bool _leap_year = is_leap_year(y);
	return get_days_until_month(_leap_year, MOONINYEAR);
}

int get_days_until_year(int y) {
	int days = 0;
	for (int i = 1; i <= y; i++){
		days += get_days_in_year(i);
	}
	if (y<1582)
		return days;
	else
		return days-10;
}

void print_text(pngwriter& pw, char* text, int pos_x, int pos_y, s_color color) {
	pw.plot_text(printer.font_name, printer.font_size, pos_x, (printer.img_h - pos_y),
		0, text, color.r, color.g, color.b);
}

void print_text( pngwriter& pw, char* text, int pos_x, int pos_y ) {
	print_text(pw, text, pos_x, pos_y, s_color(printer.default_r, printer.default_g, printer.default_b,1.0f) );
}

s_color define_day_color(int week_index,int day_index, int total_day) {
	s_color color = c_color::Black;
	if (week_index == 0) {
		if (total_day == 7 && day_index == 0)
			color = c_color::Red;
		else if (day_index == total_day - 2)
			color = c_color::Green;
	}
	else if (day_index == 0) {
		color = c_color::Red;
	}
	else if (day_index == 5) {
		color = c_color::Green;
	}
	return color;
}

void print_aday(pngwriter& pw, char* text, int pos_x, int pos_y, s_color color,int p_index) {
	int _nday = atoi(text);
	if (_nday < 10) {
		char ntext[3]; ntext[0] = '0'; ntext[1] = text[0]; ntext[2] = '\0';
		print_text(pw, ntext, pos_x, pos_y, color);
	}
	else {
		print_text(pw, text, pos_x, pos_y, color);
	}
	pw.plot_text(printer.font_name, printer.pasaran_font_size, pos_x + printer.pasaran_adjust_x,
		(printer.img_h - pos_y) - printer.pasaran_adjust_y,
		0, pasaran_name[p_index], color.r, color.g, color.b);
}

void print_days(char** days, int week_index, int day_total, pngwriter& pw, int pos_x,
	int pos_y, int* pasaran, int curr_month) {
	int px = pos_x;
	if (week_index == 0) {
		int _space_day = DAYSINWEEK - day_total;
		for (int i = 0; i < _space_day; i++) {
			print_text(pw, "  ", px, pos_y + (week_index * printer.row_space) );
			px += (printer.col_space * 2);
			print_text(pw, DAY_SPACING, px, pos_y + (week_index * printer.row_space) );
			px += (printer.col_space * 2);
		}
	}
	for (int i = 0; i < day_total; i++) {
		s_color color = define_day_color(week_index, i, day_total);
		if( is_holiday(curr_month, atoi(days[i])) ) {
			color = c_color::Red;
		}
		print_aday(pw, days[i], px, pos_y + (week_index * printer.row_space), color, pasaran[i]);
		px += (printer.col_space * 2);
		print_text(pw, DAY_SPACING, px, pos_y + (week_index * printer.row_space));
		px += (printer.col_space * 2);
	}
}

void process_text(pngwriter& pw, int week_index, char* text, int pos_x, int pos_y, int* pasaran, int curr_month) {
	int _day_index  = 0;
	int _day_digit = 0;
	char _day_text[3];
	char* _days_text[DAYSINWEEK];
	while ( *text!='\0' ) {
		if ( *text != ' ' ) {
			_day_text[0] = *text++;
			_day_text[1] = *text++;
			_day_text[2] = '\0';
			_days_text[_day_index] = new char[3];
			memcpy(_days_text[_day_index++], _day_text, 3);
		}
		else {
			*text++;
		}
	}
	print_days(_days_text, week_index, _day_index, pw, pos_x, pos_y, pasaran, curr_month);

	for (int j = 0; j < _day_index; j++) {
		delete _days_text[j];
	}
}

int print_month_header(pngwriter& pw, int year, int month, int px, int py) {
	stringstream _ssmy;
	_ssmy << month_name[month - 1];
	_ssmy << " ";
	_ssmy << year;
	print_text(pw, const_cast<char*>( _ssmy.str().c_str() ), px, py);
	py = py + (printer.row_space);
	for (int i = 0; i < 7; i++) {
		print_text(pw, day_name[i], px, py );
		px += (printer.col_space * 2);
		print_text(pw, DAY_SPACING, px, py );
		px += (printer.col_space * 2);
	}
	return py;
}

void print_month_footer(pngwriter& pw, int month, int px, int py) {
	stringstream _ssfooter;
	int _n = 0;
	for (int i = 0; i < holiday_index; i++) {
		if (holidays[i].month == month) {
			_ssfooter << holidays[i].day;
			_ssfooter << " : ";
			_ssfooter << holidays[i].info;
			_ssfooter << "  ";
			_n++;
			if (_n == 2) {
				_n = 0;
				pw.plot_text(printer.font_name, printer.pasaran_font_size, px, (printer.img_h - py), 0,
					const_cast<char*>(_ssfooter.str().c_str()), c_color::Blue.r, c_color::Blue.g, c_color::Blue.b);
				_ssfooter.str("");
				_ssfooter.clear();
				py += printer.month_footer_space;
			}
		}
	}
	pw.plot_text(printer.font_name, printer.pasaran_font_size, px, (printer.img_h - py), 0,
		const_cast<char*>(_ssfooter.str().c_str()), c_color::Blue.r, c_color::Blue.g, c_color::Blue.b);
}

void print_month(pngwriter& pw, int year, int month, int total_days_prev_year, int ipx, int ipy ) {
	int _year_target = year;
	int _moon_target = month;

	int _printed_month = _moon_target - 1;
	bool _leapYear = is_leap_year(_year_target);
	int _prev_day = get_days_until_month(_leapYear, _printed_month) + total_days_prev_year;
	int _length_day_in_month = day_in_month[_printed_month];
	int _start_d = _prev_day + 1;
	int _end_d = _start_d + _length_day_in_month;

	int _week_index = 0;
	std::stringstream _stream;
	int px = ipx;
	int py = ipy;
	py = print_month_header(pw, _year_target, _moon_target, px, py);
	py = py + (printer.row_space);
	int* _pasaran = new int[7];
	int _pasaran_index = 0;
	for (int i = _start_d; i < _end_d; ++i) {
		int n = i - _prev_day;
		int mod = (i % 7);
		_pasaran[_pasaran_index++] = (i % 5);
		_stream << n << " ";
		if (mod == 1) {
			std::string s;
			s = _stream.str();
			process_text(pw, _week_index, const_cast<char*>(s.c_str()), px, py, _pasaran, _moon_target);
			_stream.str("");
			_stream.clear();
			_week_index++;
			_pasaran_index = 0;
		}
	}
	std::string s = _stream.str();
	process_text(pw, _week_index, const_cast<char*>(s.c_str()), px, py, _pasaran, _moon_target);
	if ( strcmp(s.c_str(), "") == 0 )
		_week_index -= 1;
	print_month_footer(pw, _moon_target, px, py+((_week_index )*printer.row_space)+printer.pasaran_adjust_y*3 );
	delete[] _pasaran;
}

void print_month(pngwriter& pw ,int year ,int month, int px, int py) {
	int _prev_day = get_days_until_year(year - 1);
	print_month( pw, year, month, _prev_day, px, py );
}

void print_year(pngwriter& pw, int year, int px, int py) {
	int ncol = 4;
	int nrow = 3;
	int nth_month = 1;

	int _prev_day = get_days_until_year(year - 1);
	int _px = px;
	int _py = py;
	for (int i = 0; i < nrow; i++) {
		for (int j = 0; j < ncol; j++) {
			print_month(pw, year, nth_month, _prev_day, _px, _py);
			nth_month++;
			_px += printer.month_horizontal_space;
		}
		_py += printer.month_vertical_space;
		_px = px;
	}
}

void add_holiday(int m, int d, char * info) {
	holidays[holiday_index].day = d;
	holidays[holiday_index].month = m;
	holidays[holiday_index].info = info;
	holiday_index++;
}

static int config_handler(void* user, const char* section, const char* name,
	const char* value)
{
	#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
	#define MATCH2(s, n) strcmp(section, s) == 0

	s_printer* _pprinter = (s_printer*)(user);

	if (MATCH("setting", "print_type")) {
		_pprinter->print_type = atoi(value);
	}
	else if (MATCH("setting", "year_target")) {
		_pprinter->year_target = atoi(value);
	}
	else if (MATCH("setting", "month_target")) {
		_pprinter->month_target = atoi(value);
	}
	else if (MATCH("setting", "background_img")) {
		_pprinter->background_img = strdup(value);
	}
	else if ( MATCH("setting", "font_size") ) {
		_pprinter->font_size = atoi(value);
	}
	else if (MATCH("setting", "pasaran_font_size")) {
		_pprinter->pasaran_font_size = atoi(value);
	}
	else if (MATCH("setting", "moon_title_font_size")) {
		_pprinter->moon_title_font_size = atoi(value);
	}
	else if (MATCH("setting", "font_name")) {
		_pprinter->font_name = strdup(value);
	}
	else if (MATCH("setting", "start_x")) {
		_pprinter->start_x = atoi(value);
	}
	else if (MATCH("setting", "start_y")) {
		_pprinter->start_y = atoi(value);
	}
	else if (MATCH("setting", "pasaran_adjust_x")) {
		_pprinter->pasaran_adjust_x = atoi(value);
	}
	else if (MATCH("setting", "pasaran_adjust_y")) {
		_pprinter->pasaran_adjust_y = atoi(value);
	}
	else if (MATCH("setting", "row_space")) {
		_pprinter->row_space = atoi(value);
	}
	else if (MATCH("setting", "col_space")) {
		_pprinter->col_space = atoi(value);
	}
	else if (MATCH("setting", "month_horizontal_space")) {
		_pprinter->month_horizontal_space = atoi(value);
	}
	else if (MATCH("setting", "month_vertical_space")) {
		_pprinter->month_vertical_space = atoi(value);
	}
	else if (MATCH("setting", "month_footer_space")) {
		_pprinter->month_footer_space = atoi(value);
	}
	else if (MATCH("setting", "default_r")) {
		_pprinter->default_r = atoi(value);
	}
	else if (MATCH("setting", "default_g")) {
		_pprinter->default_g = atoi(value);
	}
	else if (MATCH("setting", "default_b")) {
		_pprinter->default_b = atoi(value);
	}
	else if (MATCH("setting", "img_w")) {
		_pprinter->img_w = atoi(value);
	}
	else if (MATCH("setting", "img_h")) {
		_pprinter->img_h = atoi(value);
	}
	// holidays parsing
	else if (MATCH2("January")) {
		add_holiday(1, atoi(name), strdup(value));
	}
	else if (MATCH2("February")) {
		add_holiday(2, atoi(name), strdup(value));
	}
	else if (MATCH2("March")) {
		add_holiday(3, atoi(name), strdup(value));
	}
	else if (MATCH2("April")) {
		add_holiday(4, atoi(name), strdup(value));
	}
	else if (MATCH2("May")) {
		add_holiday(5, atoi(name), strdup(value));
	}
	else if (MATCH2("June")) {
		add_holiday(6, atoi(name), strdup(value));
	}
	else if (MATCH2("July")) {
		add_holiday(7, atoi(name), strdup(value));
	}
	else if (MATCH2("August")) {
		add_holiday(8, atoi(name), strdup(value));
	}
	else if (MATCH2("September")) {
		add_holiday(9, atoi(name), strdup(value));
	}
	else if (MATCH2("October")) {
		add_holiday(10, atoi(name), strdup(value));
	}
	else if (MATCH2("November")) {
		add_holiday(11, atoi(name), strdup(value));
	}
	else if (MATCH2("December")) {
		add_holiday(12, atoi(name), strdup(value));
	}
	return 0;
}

int get_config(char* conf_name) {
	if (ini_parse(conf_name, config_handler, &printer) < 0) {
		printf("can't load file : %s\n", conf_name);
		return 1;
	}
	return 0;
}

int main(int argc, char** argv) {
	printf("console app to generate calendar from year - month input\n");
	printf("the output is png image\n");
	printf("(c) 2014 edi ermawan <edi.ermawan@gmail.com>\n");
	printf("use : ccm.exe configfilename.ini\n");
	if (argc > 1) {
		if ( get_config(argv[1]) ) {
			printf("error parse config file\n");
			return 1;
		}
	}
	else {
		printf("use : ccm.exe configfilename.ini");
		return 1;
	}

	pngwriter* _png;
	if ( strcmp(printer.background_img,"none") )  {
		_png = new pngwriter();
		_png->readfromfile( printer.background_img );
		_png->pngwriter_rename(CALOUT);
		printer.img_w = _png->getwidth();
		printer.img_h = _png->getheight();
	}
	else {
		_png = new pngwriter(printer.img_w, printer.img_h, 1.0, CALOUT);
	}
	if (printer.print_type == 1) {
		print_month(*_png, printer.year_target, printer.month_target, printer.start_x, printer.start_y);
		printf("done ..!");
	}
	else if (printer.print_type == 0) {
		print_year(*_png, printer.year_target, printer.start_x, printer.start_y);
		printf("done ..!");
	}
	_png->setcompressionlevel(6);
	_png->setgamma(0.7);
	_png->close();
	delete _png;

	return 0;
}

selanjutnya mungkin saya akan post full project ke svn google code / github .

// edi ermawan , Jkt 16/11/2014

Written by XediXermawan

November 16, 2014 at 4:13 pm

Mengganti power supply

with 2 comments

Beberapa malam hari yang lalu, saat lagi menggunakan komputer, secara tidak sengaja, kaki saya menyentuh kabel power dari power supply, dan tiba tiba komputer saya mati, dibarengi dengan percikan api. Cukup membuat kaget. Kecelakaan kecil yang mengkawatirkan . Saya coba lagi nyalakan 2-3 kali, tidak ada reaksi. Benar benar mati. Tidak ada yang saya lakukan setelah itu, karena saya langsung tidur. Saat akhir pekan, saya coba tes lagi kondisi PC, siapa tau ada keajaiban, haha. Ya, tentu saja, masih sama, tidak mau menyala. Mulai kawatir, semoga bukan motherboard/cpu yang rusak. Dugaan pertama saya adalah : power supply. Rencananya, PC nya akan saya bawa ke toko komputer di mall dekat sini ( perkiraan 2-3 km ), namun karena CPU nya lumayan berat , saya batalkan. Sebenarnya yang bikin berat casing nya. Ok, saya mencoba lepas power supplynya . Saya cari obeng, hmm untungnya nemu obeng di dalam tasku. (selain itu saya tidak punya peralatan, karena saya baru datang di kota baru ini ). Saya tidak ingat secara pasti , kenapa ada obeng ditasku 😁 . Saya sudah lama tidak buka buka casing komputer, terakhir kali saat saya masih pakai pentium 3 700 mhz ( tahun 2009 ). Namun ternyata tidak jauh berbeda dengan komputer pc model baru. Sedikit masalah yang saya hadapi : kabel supply nya disusun lumayan rapi oleh pak perakit dulu, ini artinya apa ?, : kabelnya lumayan susah untuk dilepas ! , butuh waktu 1,5 jam untuk melepasnya. Setelah lepas, saya tes power supply nya, apakah benar benar rusak atau tidak. Cukup koneksikan kabel hijau dan hitam, dan lihat kipasnya berputar atau tidak. Sedikit berita gembira :power supplynya yang rusak. Saya tidak mengecek lebih dalam bagian mana yang rusak , kalau yang rusak sekering, mungkin masih bisa diganti. Keesokan harinya, saya mencari power supply pengganti di toko komputer. Cukup hati hati memilih watt ( juga pure wattnya ) dan merk power supply, karena dia yang akan mengaliri daya ke komponen penting lain, yang harganya lebih mahal : mobo, cpu, video card , memory . Akhirnya dapat juga power supply yang cocok .( setelah keliling putar putar mencari jalan menuju & keluar dari mall gede ga jelas *sigh* ) . Setelah sampai di kos, saya langsung pasang ke komputer. Saya kira memasang akan lebih susah, ternyata tidak, dan yuhuuu, komputerku yang super berisik menyala kembali. Haha. Semoga power supply nya oke oke aja . Beberapa screenshoot. tengah : power supply yang baru .

Written by XediXermawan

October 5, 2014 at 3:18 pm

Posted in computer related

Tagged with

Rendering animated GIF file

with 3 comments

gif file ( http://en.wikipedia.org/wiki/Graphics_Interchange_Format ) is very popular format , you can share funny animation, emotion, a splash for ads, etc. All web browser support this format. do you interested in how game or application (non-web) render animated gif ? . here my experiment result :

( set video quality to 480p  )

info :
rendering : DirectX 11
language : c/c++
GIF decoder : libnsgif

Written by XediXermawan

May 30, 2014 at 12:31 am

Posted in C/C++ Diary

Tagged with , , ,

tcp client server dengan winsock2

leave a comment »

Untuk keperluan selanjutnya di pengembangan game yang sedang saya buat ( 😮 ), saya memerlukan program yang bisa di tweak lewat program lain. Dengan kata lain 2 program yang saling bisa berkomunikasi. Cara yang bisa dicapai , pakai file sharing atau dengan cara yang pas : pakai networking tcp / udp . Kalau mau buat program dengan kemampuan networking, cara yang bijaksana mungkin pakai 3rd party library yang sudah cukup populer dan reliable. Di antaranya, yang saya ketahui dan sepertinya sudah terbukti bagus : boost-asio ( free ), raknet ( free untuk hobyist ), libevent ( free ), etc. ada yang lain ?. Tapi saya pinginnya ndak usah pakai 3rd party lib, lagipula program yang akan saya buat cukup sederhana. winsock2 & c/c++ . itu pilihannya. dan rasanya akan fun juga buat program nya hehe. Sebagai pemanasan awal dengan winsock2 ( saya pemula ) , saya perlu membuat program seperti ini : ada client dan server. client bisa kirim data ke server. server bisa kirim data ke client. jika client nya lebih dari satu, client bisa kirim data ke client yang lain. representasi dari program ini adalah program “chatting” :d .
kemampuan server dalam keadaan running :
– stand by & listening kalau ada client mau membuat koneksi.
– setiap ada client tersambung, server akan membuat handler yang menangani koneksi ke client tersebut berdasarkan socket id. ( dalam test program dipost ini, setiap ada client, server membuat thread baru, untuk menanganinya ).
– server bisa kirim data ke client lewat console
– ada > 3 thread . 1 main thread: untuk menangkap command line dari user, 1 thread untuk “stand by” / connection acceptor, dan n thread , untuk n client yang tersambung. ya sebenarnya ndak benar ( bisa di optimize pakai thread pool mungkin nantinya ).

kemampuan client dalam keadaan running :
– connect ke server
– kirim data ke server
– ada 2 thread, 1 main thread, 1 client handler thread ( data receiver & sender )
– port hardcoded : 22536

catatan :
[1] saya membuat socket mode ke non-blocking , artinya pemanggilan fungsi2 winsock seperti connect, recv, send tidak akan mem-blok jalannya program loop. jadi non-blocking & di thread lain selain main thread.
u_long iMode = 1;  // iMode=0, blocking mode, default value
ioctlsocket(ConnectSocket, FIONBIO, &iMode);
[2] untuk sharing data antar thread, saya menggunakan concurrent queue dari blog Anthony William http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html yang saya modifikasi menggunakan std library ( awalnya pakai boost )
[3] data yang dikirim berupa string.

Beberapa pertanyaan :
– apa cara di catatan no [1] ( non-blocking & di thread lain ) efektif ?
– saat memakai non-blocking mode, kalau mengirim data byte yang sangat besaarr , apakah semuanya terkirim langsung ATAU setelah beberapa call sent?

Reference :
MSDN winsock2 ( belum semuanya saya baca 😐 )

berikut code program chat client & server yang saya buat :

(server code) : ~288 baris

// xedi xermawan < edi.ermawan@gmail.com >
// May 27, 2014
// tcp client server networking test using winsock2
// server code

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>

#include "concurrent_queue.hpp"

#pragma comment (lib, "ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "22536"

struct XDclient_info
{
	int socket_id;
	struct sockaddr_in address;
};

class XDclient
{
public:
	XDclient(XDclient_info& clientinfo)
	{
		m_clientinfo = clientinfo;
		m_recvbuflen = DEFAULT_BUFLEN;
		m_islive = true;
		m_thread = new std::thread(&XDclient::run, this);
	}
	bool isLive()
	{
		return m_islive;
	}
	XDclient_info getinfo() const
	{
		return m_clientinfo;
	}
	~XDclient()
	{
		m_thread->join();
		delete m_thread;
	}
	std::string get_data()
	{
		std::string ret_str = "";
		if (!m_queue.empty())
		{
			m_queue.try_pop(ret_str);
		}
		return ret_str;
	}
private:
	XDclient_info m_clientinfo;
	std::thread* m_thread;
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_islive;
	concurrent_queue <std::string> m_queue;

	void run()
	{
		while (m_islive)
		{
			unsigned int result_recv = recv( m_clientinfo.socket_id, m_recvbuf, m_recvbuflen, 0);
			int nError = WSAGetLastError();
			if (nError != WSAEWOULDBLOCK&&nError != 0)
			{
				printf("Client Error: %d\n", nError);
				printf("Client disconnected: %d\n", nError);
				m_islive = false;
			}
			else if (result_recv != INVALID_SOCKET && result_recv!= 0)
			{
				m_recvbuf[result_recv] = '\0';
				std::string str_send(m_recvbuf);
				m_queue.push(str_send);
			}
			else if (result_recv == 0)
			{
				printf("Client disconnected: %d\n", nError);
				m_islive = false;
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
};

class XDserver
{
public:
	XDserver()
	{
		m_recvbuflen = DEFAULT_BUFLEN;
		m_isrun = true;
		m_thread = new std::thread(&XDserver::run, this);
	}
	~XDserver()
	{
		m_isrun = false;
		m_thread->join();
		delete m_thread;
	}
	void run()
	{
		WSADATA wsaData;
		int result_ ;

		SOCKET listen_socket = INVALID_SOCKET;
		SOCKET client_socket = INVALID_SOCKET;

		struct addrinfo *result = NULL;
		struct addrinfo hints;

		char recvbuf[DEFAULT_BUFLEN];
		int recvbuflen = DEFAULT_BUFLEN;

		// init
		result_ = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (result_ != 0) {
			printf("WSAStartup failed with error: %d\n", result_);
		}

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;
		hints.ai_flags = AI_PASSIVE;

		// get info address for host
		result_ = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
		if (result_ != 0) {
			printf("getaddrinfo failed with error: %d\n", result_);
			WSACleanup();
		}
		// create socket
		listen_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
		if (listen_socket == INVALID_SOCKET) {
			printf("socket failed. error code : %ld\n", WSAGetLastError());
			freeaddrinfo(result);
			WSACleanup();
		}
		// set-up socket listening
		result_ = bind(listen_socket, result->ai_addr, (int)result->ai_addrlen);
		if (result_ == SOCKET_ERROR) {
			printf("bind failed. error code: %d\n", WSAGetLastError());
			freeaddrinfo(result);
			closesocket(listen_socket);
			WSACleanup();
			m_isrun = false;
		}
		if (!m_isrun)
			return;
		freeaddrinfo(result);

		result_ = listen(listen_socket, SOMAXCONN);
		if (result_ == SOCKET_ERROR) {
			printf("listen failed. error code: %d\n", WSAGetLastError());
			closesocket(listen_socket);
			WSACleanup();
		}
		// set to non-blocking mode
		u_long iMode = 1;
		ioctlsocket(listen_socket, FIONBIO, &iMode);
		int t_clientAddressLen = sizeof(struct sockaddr_in);
		struct sockaddr_in t_clientAddress;
		// server loop
		while (m_isrun)
		{
			// check new client connected
			client_socket = accept(listen_socket, (struct sockaddr *)&t_clientAddress, &t_clientAddressLen);
			int nError = WSAGetLastError();
			if (nError != WSAEWOULDBLOCK&&nError != 0)
			{
				// todo :
			}
			else if (client_socket != INVALID_SOCKET)
			{
				XDclient_info clientinfo;
				clientinfo.address = t_clientAddress;
				clientinfo.socket_id = client_socket;

				XDclient* clientnew = new XDclient(clientinfo);
				m_clients.push_back(clientnew);
				if (client_socket == INVALID_SOCKET) {
					printf("accept failed. error code: %d\n", WSAGetLastError());
				}
			}
			// check client live
			for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
			{
				if (!(*it)->isLive())
				{
					delete *it;
					m_clients.erase(it);
					break;
				}
			}
			// check if there are data need to be sent
			if (!m_queue.empty())
			{
				std::string str = "";
				m_queue.try_pop(str );
				std::cout << " send data: " << str.c_str() <<std::endl;
				for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
				{
					int m_recvbuflen;
					int sendresult = send((*it)->getinfo().socket_id, str.c_str(), str.length(), 0);
					if (sendresult == SOCKET_ERROR)
					{
						std::cout << " send failed. error code " << sendresult << std::endl;
					}
				}
			}
			for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
			{
				std::string str_data = (*it)->get_data();
				if (str_data != "")
				{
					for (std::vector< XDclient* >::iterator it2 = m_clients.begin(); it2 != m_clients.end(); it2++)
					{
						if (*it != *it2)
						{
							int sendresult = send((*it2)->getinfo().socket_id, str_data.c_str(), str_data.length(), 0);
							if (sendresult == SOCKET_ERROR)
							{
								std::cout << " send failed. error code " << sendresult << std::endl;
							}
						}
					}
				}
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
	void senddata(std::string& str)
	{
		m_queue.push(str);
	}
	bool isLive()
	{
		return m_isrun;
	}
private:
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_isrun;
	std::thread* m_thread;
	std::vector< XDclient* > m_clients;
	concurrent_queue <std::string> m_queue;
};

class Games
{
};

int __cdecl main(int argc, char** argv)
{
	XDserver* server = new XDserver();
	std::cout << "welcome to the server"<<std::endl;
	std::cout << "type an input to broadcast to all client. ""exit"" to close program " << std::endl;
	while (server->isLive())
	{
		char str_in[512]="";
		std::cin >> str_in;
		std::string str_input(str_in);
		server->senddata("server|"+str_input);
		if (!str_input.compare("exit"))
		{
			break;
		}
	}
	delete server;
	std::cout << "server shutdown" << std::endl;
	system("pause");
	return 0;
}

(client  code) :  ~181 baris

// xedi xermawan < edi.ermawan@gmail.com >
// May 27, 2014
// tcp client server networking test using winsock2
// client code

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <sstream>

#include "concurrent_queue.hpp"

// link to winsock2 static lib
#pragma comment (lib, "ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "22536"

class XDclientapp
{
public:
	XDclientapp(std::string& clientname, std::string& serveraddresss)
	{
		m_server_address = serveraddresss;
		m_client_name    = clientname;
		m_recvbuflen = DEFAULT_BUFLEN;
		m_isrun = true;
		m_thread = new std::thread(&XDclientapp::run, this);
	}
	~XDclientapp()
	{
		m_isrun = false;
		m_thread->join();
		delete m_thread;
	}
	void run()
	{
		WSADATA wsaData;
		SOCKET ConnectSocket = INVALID_SOCKET;
		struct addrinfo *result = NULL,
			*ptr = NULL,
			hints;
		char *sendbuf = "this is a test";
		char recvbuf[DEFAULT_BUFLEN];
		int iResult;
		int recvbuflen = DEFAULT_BUFLEN;

		// Initialize Winsock
		iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (iResult != 0) {
			printf("WSAStartup failed with error: %d\n", iResult);
		}

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;

		// Resolve the server address and port
		iResult = getaddrinfo( m_server_address.c_str(), DEFAULT_PORT, &hints, &result);
		if (iResult != 0) {
			printf("getaddrinfo failed with error: %d\n", iResult);
			WSACleanup();
		}

		// Attempt to connect to an address until one succeeds
		for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

			// Create a SOCKET for connecting to server
			ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
				ptr->ai_protocol);
			if (ConnectSocket == INVALID_SOCKET) {
				printf("socket failed with error: %ld\n", WSAGetLastError());
				WSACleanup();
			}

			// Connect to server.
			iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
			if (iResult == SOCKET_ERROR) {
				closesocket(ConnectSocket);
				ConnectSocket = INVALID_SOCKET;
				continue;
			}
			break;
		}
		// set to non-blocking mode
		u_long iMode = 1;
		ioctlsocket(ConnectSocket, FIONBIO, &iMode);
		freeaddrinfo(result);
		int errorid;
		while (1)
		{
			unsigned int result_recv = recv(ConnectSocket, recvbuf, recvbuflen, 0);

			errorid = WSAGetLastError();
			if (errorid != WSAEWOULDBLOCK && errorid != 0)
			{
				printf("Client disconnected: %d\n", errorid);
				break;
			}
			else if (result_recv != INVALID_SOCKET && result_recv != 0)
			{
				// printf("Bytes received: %d\n", result_recv);
				recvbuf[result_recv] = '\0';
				printf("\n%s\n", recvbuf);
				std::cout << m_client_name.c_str() << "(You) : ";
			}
			else if (result_recv == 0)
			{
				printf("Client disconnected: %d\n", errorid);
				break;
			}

			// check if there are data need to be sent
			if (!m_queue.empty())
			{
				std::string str = "";
				m_queue.try_pop(str);
				int m_recvbuflen;
				int sendresult = send( ConnectSocket , str.c_str(), str.length(), 0);
				if (sendresult == SOCKET_ERROR)
				{
					std::cout << " send failed. error code " << sendresult << std::endl;
				}
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
	void senddata(std::string& str)
	{
		std::string pstr = m_client_name + " : " + str;
		m_queue.push(pstr);
	}
private:
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_isrun;
	std::thread* m_thread;
	concurrent_queue <std::string> m_queue;
	std::string m_client_name;
	std::string m_server_address;
};
//--
int __cdecl main(int argc,char** argv)
{
	std::string clientname = "";
	std::string serveraddress = "localhost";
	std::cout << "Please enter your name: ";
	std::getline(std::cin, clientname);

	if (argc > 1)
	{
		serveraddress = argv[1];
	}
	XDclientapp* client = new XDclientapp(clientname, serveraddress);
	std::cout << "welcome to the chat client" << std::endl;
	std::cout << "type an input to broadcast to all client. ""exit"" to close program " << std::endl;
	std::cout << "---" << std::endl;
	while (1)
	{
		std::string str_input = "";
		std::cout << clientname.c_str() << "(You) : ";
		std::getline(std::cin, str_input);

		client->senddata(str_input);
		if (!str_input.compare("exit"))
		{
			break;
		}
	}
	delete client;
	return 0;
}

Screenshoot :
chatprogram
Download : source + vs project + binary ( hanya 39 kb ! ) .

Written by XediXermawan

May 27, 2014 at 6:43 pm

Posted in C/C++ Diary

Tagged with , , ,

#define problem

leave a comment »

Masalah kecil tapi fatal di projects C/C++ yang  saya temui ( dan parahnya terulang-ulang ), adalah penggunaan define. #define secara salah digunakan karena dicampur aduk antara definisi & pengecekannya. sebagai contoh ada 2 define :

#define USE_FEATURE_A
#define USE_FEATURE_B (1)

Dalam penggunaanya ada beberapa kemungkinan :

#ifdef USE_FEATURE_A
// code_inside_a.1
#endif
–>penggunaan yang benar. code “code_inside_a.1” akan ter-compile jika USE_FEATURE_A ter-define. kalau ingin menghilangkan code “code_inside_a.1”, USE_FEATURE_A harus un-define dengan komen : // #define USE_FEATURE_A

#if USE_FEATURE_A
// code_inside_a.2
#endif
–> salah. dan langsung kelihatan ( compile error ) jadi tidak perlu kawatir.

#if defined(USE_FEATURE_A)
// code_inside_a.3
#endif
–> benar. code “code_inside_a.3” akan ter-compile jika USE_FEATURE_A ter-define. kalau ingin menghilangkan code “code_inside_a.3”, USE_FEATURE_A harus un-define dengan komen : // #define USE_FEATURE_A

#if USE_FEATURE_B
// code_inside_b.1
#endif
–> benar. code “code_inside_b.1” ter-compile jika #define USE_FEATURE_B (1)

#if USE_FEATURE_B == 0
// code_inside_b.2
#endif
–>benar. code “code_inside_b.2” ter-compile jika #define USE_FEATURE_B (0)

#if defined(USE_FEATURE_B)
// code_inside_b.3
#endif
->salah. walaupun “code_inside_b.3” akan ter-compile ketika #define USE_FEATURE_B (1), namun “code_inside_b.3” juga akan ter-compile jika #define USE_FEATURE_B (0)

#ifdef USE_FEATURE_B
// code_inside_b.4
#endif
–>salah. walaupun “code_inside_b.4” akan ter-compile ketika #define USE_FEATURE_B (1), namun “code_inside_b.4” juga akan ter-compile jika #define USE_FEATURE_B (0)

best practice paling baik menurut saya memakai #define USE_FEATURE_B (1) dan secara konsisten menggunakan #if untuk mengecek.

 

Written by XediXermawan

May 21, 2014 at 5:22 pm

Posted in C/C++ Diary

Tagged with ,

my recent spare time projects

leave a comment »

I have been working on these projects in my spare time quite while ( like I work on it when I want work on it  :p )., but recently I’m disappointed with my progress ( apparently I’m busy and have difficulty when creating graphics assets by myself ). so I decided to upload source code to google code in case I’m interested again next time .There are 2 projects :

[1] unknown project 1. very simple game, almost done. keyword : C/C++,DirectX, Desktop App,Windows Phone 8

home project : https://code.google.com/p/my-fifteen-puzzle-dx/

svn link : https://my-fifteen-puzzle-dx.googlecode.com/svn/trunk

[2] unknown project 2 . also very simple game ( I like simple thing ). puzzle. gameplay complete. keyword : C/C++,DirectX, Desktop App, Windows Phone 8.

home project : https://code.google.com/p/proximity-game-clone2-dx/

svn link : https://proximity-game-clone2-dx.googlecode.com/svn/trunk

both of them actually same app I had made before . I rewrite again these projects using entirely new c/c++ code and using DirectX as graphics API. I also implemented some design pattern there. In case you are interested , check it out 🙂

game screenhoot running on windows phone 8 device
fiveteen_wp8

Written by XediXermawan

May 13, 2014 at 11:55 am