Thursday, September 18, 2014

FlashBuilder에서 FlexFormatter 사용하기

FlashBuilder에서는 Flex 소스의 Formatting을 해주는 FlexFormatter라는 플러그인이 있다.
이 플러그인을 설정하면 여러가지 소스를 단일한 코딩 스타일로 맞출 수가 있다.

메뉴 [Help] - [Install New Software]를 클릭

Add Repository를 하여 FlexFormatter를 수동입력하고 다음과 같이 주소를 입력
http://flexformatter.googlecode.com/svn/trunk/FlexFormatter/FlexPrettyPrintCommandUpdateSite 


이제 주소가 생겼으니 Select All하여 설치하자




다 설치하면 위에 /*/* /* ... Fx 이런 툴바가 새로 뜬다. 이게 FlexFormatter 관련 툴바이다.

Preferences에서도 FlexFormatter가 있다. 나는 개인적으로 Tab을 Space로 하는 것을 좋아하니 설정한다.

뭐 기타 잡다한 코딩 스타일을 설정할수 있다.

단 ActionScript(*.as)와 MXML(*.mxml)이 분리되어 있다.

써보면 정말 좋다는걸 알수 있다.

이클립스가 단축키가 Visual Studio와 달라서 코딩스타일 맞추는데 애먹는데 이것을 사용하면 일단 코딩 후에 나중에 다 맞출 수 있다.

Flash ActionScript에서 Object ID 확인하기

어떤 오브젝트의 메모리 주소 포인터(@b123c11) 또는 유니크한 ID를 보고 싶을때가 있다.
메모리 주소를 AcrionScript에서 보기위해서는 다음과 같은 클래스를 작성하여 호출하면 된다.

package Lib
{
public class DebugUtils
{
public static function getObjectMemoryHash(obj:*):String
{
var memoryHash:String;
try
{
FakeClass(obj);
}
catch (e:Error)
{
memoryHash = String(e).replace(/.*([@|\$].*?) to .*$/gi, '$1');
}
return memoryHash;
}
}
}
internal final class FakeClass { }

모바일 게임 서버 제작 방법

안녕하세요? 비니아빠 바야바입니다.
일단 조회수를 위해 제목만 거창하게 달아봤습니다. 죽여주세요.
오늘은 제가 만든 온라인 게임들은 어떻게 만들어졌는지 진짜 간단하게 소개해보려고 합니다.
2G->3G->LTE를 거치면서 모바일 네트워크 성능은 점점 더 진화하고 있고,
앞으로 모바일 온라인 게임이 마켓의 트렌드가 자리잡을 날도 머지 않았기 때문입니다.

1. 게임 서버
알까기 온라인의 초창기 시절에는 집에서 개인PC 한대를 서버 전용으로 돌렸습니다.
동접이 늘어나서 향후에는 17만원 상당의 서버 호스팅 서비스를 이용했구요.
알까기, 장기, 오목, 탱크가디언까지 4개 온라인 게임을 1대의 서버에서 모두 실행했습니다.
최근에 개발한 치킨팝 온라인은 KT의 클라우드 서비스를 이용하고 있는데 성능은 괜찮습니다만,
KT측의 클라우드 서비스가 죽어서 2번 정도 서비스가 중단되는 사태가 있었습니다.

게임 서버는 윈도우 서버를 사용했고, Visual C/C++를 이용해 서버당 동접 3천명을 처리하도록 개발했습니다.
알까기는 최대 1700명을 기록했고, 장기가 900명, 오목이 250명, 탱크는 150명 수준을 기록했습니다.

게임 서버의 스레드는 이벤트 방식으로 원스레드 방식을 썼습니다.
Z9별이라는 MMORPG를 개발할 당시에는 멀티스레드를 사용했습니다만, 제가 만든 게임들은 모두 턴 게임였기 때문에
필요성이 느껴지지 않아서 그냥 원스레드로 개발했고, 성능에도 아무런 문제가 없었습니다.

2. 데이터 베이스
게임의 DB는 윈도우 서버라서 MS-SQL을 썼습니다.
다만 서버 호스팅 비용보다 MS-SQL 임대 비용이 더 비싼 관계로, 월 6,900원짜리 DB호스팅을 이용했습니다.
그럭저럭 사용할 만한 수준은 됐습니다만, 역시 전용 DB가 없으니 속도상 느린 것은 어쩔 수 없더군요.
DB호스팅을 한 후에, 게임 서버와 ODBC로 연결하여 쿼리를 날렸습니다.
MySQL이 더 편하신 분들은 MySQL을 쓰셔도 아무런 문제가 없습니다.

3. 소켓과 프로토콜
온라인 게임을 만들려면 서버에서 TCP 소켓을 열고 패킷을 주고 받아야 합니다.
제가 자바를 할줄 몰라서 그렇긴 합니다만, 자바로도 충분히 게임 서버를 만들 수 있을걸로 생각됩니다.
게임 서버에서 TCP 소켓을 열고, 패킷을 주고받을 프로토콜이 완성되면
안드로이드 클라이언트에서 서버와의 통신을 위한 스레드를 하나 만듭니다.
게임이 돌아가는 동안에도 네트워크와 계속 통신하면서 데이터를 보내고 받아오고 해야 하기 때문입니다.
socket.setSoTimeout( 60000 );
socket.setTcpNoDelay( true );
저는 클라이언트의 소켓 옵션을 2가지 지정했습니다.
모바일의 네트워크 상태는 아직 불안정하기 때문에 setSoTimeout 시간을 60초 정도 지정해주었고,
setTcpNoDelay를 true로 셋팅해서 패킷을 다 기다리지 않고 곧바로 보내도록 지정했습니다.

서버에서 오는 패킷을 받기 위해 10000 바이트의 버퍼를 만들어서 데이터를 쌓았습니다.
쌓여진 데이터의 선두부터 체크해서 1개의 패킷이 구성될 수 있을 만큼 데이터가 쌓였으면 패킷을 처리하고
다음 패킷의 크기만큼 쌓일때까지 계속 스레드를 돕니다.

여기서 중요한건 통신 스레드에 Sleep을 어느정도 걸어줘야 한다는 겁니다.
안그러면 폰에 따라 게임의 속도에도 영향을 주게 됩니다. 저 같은 경우는 Thread.sleep( 100 );을 해줬습니다.

4. 데이터 구조
저는 서버와 주고 받은 모든 값을 byte형 배열로 처리했습니다.
패킷에 char, short, int 등의 데이터를 byte 단위로 쉬프트 연산해서 1바이트씩 쪼개서 배열에 차례대로 넣고,
문자열을 보내야 할 경우에도 문자열을 byte형 데이터로 바꾸어서 전체 길이 만큼 배열에 추가시켰습니다.
서버에서 보내지는 데이터도 당연히 byte 배열이고, 배열 내용에서 다시 char, short, int, 문자열 등을 추출합니다.
한가지 기억해두셔야 할 것은 안드로이드가 사용하는 문자열은 UTF-8이고, 윈도우 서버에서 사용하는 문자열은
아스키 코드라는 점이죠. 그래서 서버에서 받은 문자열을 사용하려면 코드 변환을 해줘야 합니다.
반대로 서버에서 클라이언트로 문자열을 보내려면 역시 반대로 코드 변환을 해줘야 하고요.

5. 온라인 게임 제작시 주의할 점
안드로이드의 네트워크 기능은 아직 불완전 합니다.
3G와 WIFI가 다 켜져 있을 경우, WIFI가 꺼지고 3G가 잡히던가 하면 서버와의 접속은 여지없이 끊어집니다.
여러가지 해결책이 있겠습니다만, 일정 시간마다 클라이언트가 서버로 라이브 패킷을 보내도록 해서
일정 시간 이상 피드백이 없으면 네트워크 상태가 고르지 못한 것으로 간주하고 연결을 끊어버리거나,
게임 화면을 그대로 유지하면서 접속만 다시 시도하는 방법 등이 있겠습니다.
온라인 게임은 유저끼리 승패를 겨루는 경우가 많기 때문에 이런 처리가 없으면 상대방이 연결이 끊어졌을때,
다른 한쪽은 하염없이 계속 기다리고 있어야 하는 불상사가 생깁니다.
연결이 끊어지면 끊어졌다라는 신호라도 서버로 바로 보내주면 좋으련만, 안드로이드는 그런 친절함은 없어 보입니다.

이상입니다.
온라인 게임 개발에 대해 궁금하셨던 분들에게 약간이나마 도움이 되셨길 바랍니다.

http://www.androidpub.com/2048035

TOW 윈도우8.1 실행 에러

TOW를 start-tow.bat로 윈도우8.1에서 실행할 경우에 다음과 같은 에러가 나온다면

[TOW] TOW Launched.
[TOW] Now, you can test Trac in http://localhost:8080/projects/HelloTOW
[TOW] And you can test Subversion in http://localhost:8080/svn/HelloTOW
httpd: Syntax error on line 493 of C:/TOW/Apache/conf/httpd.conf: Cannot load C:/TOW/Apache/modules/mod_python.so into server: \xc1\xf6\xc1\xa4\xb5\xc8 \xb8\xf0\xb5\xe2\xc0\xbb \xc3\xa3\xc0\xbb \xbc\xf6 \xbe\xf8\xbd\xc0\xb4\xcf\xb4\xd9.

아래의 위치에서 Visual Studio 2003 redistibutable을 다운받아서 설치한다.
http://duongame.blogspot.kr/2014/09/visual-c-2003-redistributable.html

TOW 새 프로젝트 생성

도스창(Command Prompt)을 띄웁니다. (실행 -> cmd)
다음과 같이 실행해봅니다. (예제 프로젝트명: MyNewProject)
   
C:\Documents and Settings\user>cd \TOW
C:\TOW>add-project MyNewProject

이렇게 하면 MyNewProject 라는 프로젝트가 생깁니다.
trac 주소는 http://서버주소:8080/projects/MyNewProject 가 됩니다.
svn 주소는 http://서버주소:8080/svn/MyNewProject 가 됩니다.

TOW SVN 기본암호

TOW(Trac On Windows)의 SVN 기본암호

ID: admin
PW: towadmin

Android MAC 주소 알아내기

WifiManager mng = (WifiManager)getSystemService(WIFI_SERVICE);
WifiInfo info = mng.getConnectionInfo();
String mac = info.getMacAddress();

Wednesday, September 17, 2014

넥서스7 USB 드라이버 다운로드

넥서스7 USB 드라이버

다운로드

삼성 USB 드라이버 다운로드

삼성 안드로이드 스마트폰 USB 드라이버

다운로드

Visual C++ 16비트 565 BMP 예제

bool loadBmpFile( LPCTSTR bmpFileName, int& outWidth, int& outHeight, int& outBpp, BYTE*& outBmpData )
{
    FILE *fp = fopen(bmpFileName, "rb");
    if(!fp)
        return false;

 BITMAPFILEHEADER bfh;
 BITMAPINFOHEADER bih;
 RGBQUAD rgb[3];

    fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fp);
    fread(&bih, sizeof(BITMAPINFOHEADER), 1, fp);

 // 16비트는 추가적으로 rgb가 12바이트 들어있다.
 if(bih.biBitCount == 16) {
  fread(rgb, sizeof(RGBQUAD), 3, fp);
 }

    LPBYTE buffer = new BYTE[bih.biSizeImage];
    fread(buffer, sizeof(BYTE), bih.biSizeImage, fp);
    fclose(fp);

 outWidth = bih.biWidth;
 outHeight = bih.biHeight;
 outBpp = bih.biBitCount;

 // height는 뒤집어 준다.
 if(outHeight < 0) {
  outHeight *= -1;
 }
 outBmpData = buffer;
 return true;
}

bool saveBmpFile( LPCTSTR bmpFileName, int width, int height, int bpp, BYTE* bmpData )
{
    PBITMAPINFO pbmi;

 if(height < 0)
  height *= -1;

 // 16비트는 추가적으로 rgb가 12바이트 들어있다.
 if (bpp == 16) {
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
        sizeof(BITMAPINFOHEADER) +
        sizeof(RGBQUAD) * 3);
 }
 else {// 24,32비트
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
        sizeof(BITMAPINFOHEADER));
 }

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = width;
    pbmi->bmiHeader.biHeight = height;
    pbmi->bmiHeader.biPlanes = 1;
    pbmi->bmiHeader.biBitCount = bpp;

 if (bpp == 16) {// 16비트 565일 경우의 bit mask를 채워준다. 그리고 rgb가 아니라 bitfields로 셋팅한다.
        pbmi->bmiHeader.biClrUsed = 3;
  pbmi->bmiHeader.biCompression = BI_BITFIELDS;
  memset(pbmi->bmiColors, 0, sizeof(RGBQUAD)*3);
  pbmi->bmiColors[0].rgbGreen = 248;
  pbmi->bmiColors[1].rgbBlue = 224;
  pbmi->bmiColors[1].rgbGreen = 7;
  pbmi->bmiColors[2].rgbBlue = 31;
 }
 else {
  pbmi->bmiHeader.biClrUsed = 0;
  pbmi->bmiHeader.biCompression = BI_RGB;
 }

    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * bpp +31) & ~31) /8
        * pbmi->bmiHeader.biHeight;
    pbmi->bmiHeader.biClrImportant = 0;

 // height는 뒤집어 준다.
 if(pbmi->bmiHeader.biHeight > 0) {
  pbmi->bmiHeader.biHeight *= -1;
 }

    HANDLE hf;                 // file handle
    BITMAPFILEHEADER hdr;       // bitmap file-header
    DWORD dwTotal;              // total count of bytes
    DWORD cb;                   // incremental count of bytes
    BYTE *hp;                   // byte pointer
    DWORD dwTmp;

    // Create the .BMP file.
    hf = CreateFile(bmpFileName,
        GENERIC_READ | GENERIC_WRITE,
        (DWORD) 0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        (HANDLE) NULL);
    if (hf == INVALID_HANDLE_VALUE)
        return false;
    hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M"
    // Compute the size of the entire file.
    hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
        pbmi->bmiHeader.biSize + pbmi->bmiHeader.biClrUsed
        * sizeof(RGBQUAD) + pbmi->bmiHeader.biSizeImage);
    hdr.bfReserved1 = 0;
    hdr.bfReserved2 = 0;

    // Compute the offset to the array of color indices.
    hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
        pbmi->bmiHeader.biSize + pbmi->bmiHeader.biClrUsed
        * sizeof (RGBQUAD);

    // Copy the BITMAPFILEHEADER into the .BMP file.
    if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
        (LPDWORD) &dwTmp,  NULL))
    {
        return false;
    }

    // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
    if (!WriteFile(hf, (LPVOID) &pbmi->bmiHeader, sizeof(BITMAPINFOHEADER)
        + pbmi->bmiHeader.biClrUsed * sizeof (RGBQUAD),
        (LPDWORD) &dwTmp, ( NULL)))
        return false;

    // Copy the array of color indices into the .BMP file.
    dwTotal = cb = pbmi->bmiHeader.biSizeImage;
    hp = bmpData;
    if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
        return false;

    // Close the .BMP file.
    if (!CloseHandle(hf))
        return false;

 return true;
}

Visual C++ 레지스트리 예제

#include "stdafx.h"
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
 wchar_t* subkey = L"Software\\RegistryTest";
 HKEY key = NULL;
 LONG ret = RegOpenKey(HKEY_LOCAL_MACHINE, subkey, &key);

 if(ret != ERROR_SUCCESS)
 {
  RegCreateKey(HKEY_LOCAL_MACHINE, subkey, &key);
 }

 wchar_t* valueName = L"fps";
 int data = 58;

 RegSetKeyValue(HKEY_LOCAL_MACHINE, subkey, valueName, REG_DWORD, &data, sizeof(int));

 DWORD dwType;
 int retData;
 DWORD cbData;

 RegGetValue(HKEY_LOCAL_MACHINE, subkey, valueName, RRF_RT_DWORD, &dwType, &retData, &cbData);

 RegCloseKey(key);

 return 0;
}

Visual C++ Release 모드에서 디버깅하기 위한 설정


Release 모드에서 디버깅을 하기 위해서 설정해야 할 환경설정 목록이다.
일반적으로 Debug information 만 생성하면 되는 줄 알았는데 그게 아니었다.

Release 모드에서 디버깅을 하는 경우가 어떤 경우가 있냐면 Release/Debug를 나누지 않고 개발할때, 아니면 긴박한 Release 모드에서의 버그를 수정할 때이다.

[Project Properties] - [Configuration Properties]에서 다음과 같은 항목을 명시된 값으로 변경한 후에 재컴파일 하면 된다.
[C/C++] - [General] - [Debug] - [Program Database (/Zi)]
[C/C++] - [Optimization] - [Optimization] - [Disabled (/Od)]
[C/C++] - [Optimization] - [Whole Program] - [No]

[Linker] - [Debugging Generate Debug Info] - [Yes (/DEBUG)]
[Linker] - [Debugging] - [Debugging] - [Runtime tracking and disable optimizations (/ASSEMBLYDEBUG)]

Visual C++ 내 IP 알아내기


#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#pragma warning(push)
#pragma warning(disable: 4127)

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>

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

#include <stdio.h>
#include <stdlib.h>

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
/* Note: could also use malloc() and free() */

// Global variables
ULONG  ulOutBufLen;
DWORD  dwRetVal;

int main() {

/* variables used for GetNetworkParams */
  FIXED_INFO  *pFixedInfo;
  IP_ADDR_STRING  *pIPAddr;

/* variables used for GetAdapterInfo */
  IP_ADAPTER_INFO  *pAdapterInfo;
  IP_ADAPTER_INFO  *pAdapter;
  UINT  dwAddrLen;

/* variables used for GetInterfaceInfo */
  IP_INTERFACE_INFO*  pInterfaceInfo;

/* variables used for GetIpAddrTable */
  MIB_IPADDRTABLE  *pIPAddrTable;
  DWORD  dwSize;
  struct in_addr  IPAddr;
  char  *strIPAddr;

/* variables used for AddIpAddress */
  UINT  iaIPAddress;
  UINT  imIPMask;
  ULONG  NTEContext;
  ULONG  NTEInstance;

/* variables used for GetIpStatistics */
  MIB_IPSTATS  *pStats;

/* variables used for GetTcpStatistics */
  MIB_TCPSTATS  *pTCPStats;

 printf("------------------------\n");
  printf("This is GetAdaptersInfo\n");
  printf("------------------------\n");


  pAdapterInfo = (IP_ADAPTER_INFO *) MALLOC( sizeof(IP_ADAPTER_INFO) );
  if (pAdapterInfo == NULL) {
    printf("Error allocating memory needed to call GetAdapterInfo\n");
    return 1;
  }
  ulOutBufLen = sizeof(IP_ADAPTER_INFO);

  if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
    FREE (pAdapterInfo);
    pAdapterInfo = (IP_ADAPTER_INFO *) MALLOC (ulOutBufLen);
    if (pAdapterInfo == NULL) {
      printf("Error allocating memory needed to call GetAdapterInfo\n");
      return 1;
    }
  }

  if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) != NO_ERROR) {
    printf("GetAdaptersInfo call failed with %d\n", dwRetVal);
    return 1;
  }
  pAdapter = pAdapterInfo;

  while (pAdapter) {
    printf("\tIP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String);
    pAdapter = pAdapter->Next;
  }
 FREE(pAdapterInfo);

  return 0;

#pragma warning(pop)

}

내 IP 알아내기(리눅스)


#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <arpa/inet.h>

int main()
{
    // 이더넷 데이터 구조체
    struct ifreq *ifr;
    struct sockaddr_in *sin;
    struct sockaddr *sa;

    // 이더넷 설정 구조체
    struct ifconf ifcfg;
    int fd;
    int n;
    int numreqs = 30;
    fd = socket(AF_INET, SOCK_DGRAM, 0);

    // 이더넷 설정정보를 가지고오기 위해서
    // 설정 구조체를 초기화하고
    // ifreq데이터는 ifc_buf에 저장되며,
    // 네트워크 장치가 여러개 있을 수 있으므로 크기를 충분히 잡아주어야 한다.
    // 보통은 루프백주소와 하나의 이더넷카드, 2개의 장치를 가진다.
    memset(&ifcfg, 0, sizeof(ifcfg));
    ifcfg.ifc_buf = NULL;
    ifcfg.ifc_len = sizeof(struct ifreq) * numreqs;
    ifcfg.ifc_buf = malloc(ifcfg.ifc_len);

    for(;;)
    {
/*        ifcfg.ifc_len = sizeof(struct ifreq) * numreqs;
        ifcfg.ifc_buf = realloc(ifcfg.ifc_buf, ifcfg.ifc_len);*/
        if (ioctl(fd, SIOCGIFCONF, (char *)&ifcfg) < 0)
        {
            perror("SIOCGIFCONF ");
            exit;
        }
        break;
    }

    ifr = ifcfg.ifc_req;
    for (n = 0; n < ifcfg.ifc_len; n+= sizeof(struct ifreq))
    {
        sin = (struct sockaddr_in *)&ifr->ifr_addr;
        printf("IP    %s\n", inet_ntoa(sin->sin_addr));
        ifr++;
    }
    close(fd);
    return 0;
}

Android NDK 예제

1. 위치 /Users/duongame/devtools/android-ndk
  1) .bash_profile에 패스를 추가해준다.
$ cat .bash_profile
export ANDROID_NDK_ROOT=/Users/duongame/devtools/android-ndk
export ANDROID_SDK_ROOT=/Users/duongame/devtools/android-sdk
export NDK_ROOT=$ANDROID_NDK_ROOT
export PATH=$PATH:$ANDROID_NDK_ROOT
  2) source .bash_profile
  3) ndk-build 실행 테스트

2. 방법
  1) 소스에 jni폴더를 만든다.
  2) 아래와 같이 소스(jni-sample1.c)를 만드는데 소스에서 함수 명은 Java_(package_name)_className_functionName으로 정한다
#include<string.h>
#include<jni.h>
jstring
Java_com_example_jnisample1_JNISample1_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
__android_log_print(ANDROID_LOG_DEBUG, "JNI", "Hello World");
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}
  3) jni폴더에 Android.mk를 만든다.
LOCAL_PATH := $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE    := jni-sample1
LOCAL_SRC_FILES := jni-sample1.c
include$(BUILD_SHARED_LIBRARY)
  4) Terminal을 열어서 해당 프로젝트의 디렉토리로 가서 ndk-build를 실행한다. 그러면 jni-sample1.so 파일이 libs 폴더 아래에 만들어 진다.
  5) 소스에서 다음과 같이 설정하면 사용할수 있다.
publicclass JNISample1 extends Activity {
    static {
        System.loadLibrary("jni-sample1");
    }
    public native String  stringFromJNI();

Android PopupWindow 전체화면


package com.example.popupwindowsample2;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.PopupWindow;

public class MainActivity extends Activity implements OnClickListener {
 private static final String TAG="MainActivity";
 private Button mBtnPopup;
 private MotionPopupWindow mMotionPopupWindow;
 private PopupWindow mPopupWindow;
 private int mTop;

 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  mBtnPopup = (Button) findViewById(R.id.btnPopup);
  mBtnPopup.setOnClickListener(this);
 }

 private void initPopupWindow() {
  if(mPopupWindow == null) {
   final LayoutInflater inflater = (LayoutInflater) MainActivity.this
     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   final View popupView = inflater.inflate(R.layout.notice, null, false);
   final Display display = getWindowManager().getDefaultDisplay();
   final int top = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
   final int width = display.getWidth();
   final int height = display.getHeight() - top;

   mTop = top;
   mPopupWindow = new PopupWindow(popupView, width, height, true);  
  }
 }

 @Override
 public void onClick(View v) {
  //Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
  initPopupWindow();
  mPopupWindow.showAtLocation(findViewById(R.id.main), Gravity.BOTTOM, mTop, 0);
 }
}

Android Maven 개발환경 설정

이 글을 쓰는 시점의 프로그램 버전
안드로이드: 17(4.2.2)
Maven: 3.1.0
이클립스: JUNO

Maven 설치: http://maven.apache.org/download.cgi
/Users/duongame/devtools/maven에 압축 풀기
Maven-eclipse (m2e) 이클립스 플러그인 설치:
http://download.eclipse.org/technology/m2e/releases
Android Connector for M2E 이클립스 플러그인 설치:
http://rgladwell.github.com/m2e-android/updates
프로젝트 마우스 오른쪽 버튼 클릭 > Configure > Convert to Maven Project > pom.xml 생성

pom.xml을 다음과 같이 편집
프로젝트 기본 package 설정
<project...
  <modelVersion>4.0.0</modelersion>
  <groupId>com.example.maventest4</groupId>
  <artifactId>MavenTest4</artifactId>
  <version>1.0</version>
  <name>MavenTest4</name>

패키징을 apk로 설정
<packaging>apk</packaging>

다음과 같은 문장을 추가+빌드에 사용할 SDK 버전을 설정
 <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <platform.version> 4.1.1.4
 </platform.version>
 <android.plugin.version>3.6.0</android.plugin.version>
 </properties>
 <dependencies>
 <dependency>
 <groupId>com.google.android</groupId>
 <artifactId>android</artifactId>
 <version>${platform.version}</version>
 <scope>provided</scope>
 </dependency>
 </dependencies>
 <build>
 <sourceDirectory>${project.basedir}/src</sourceDirectory>
 <finalName>${project.artifactId}</finalName>
 <pluginManagement>
 <plugins>
 <plugin>
 <groupId>com.jayway.maven.plugins.android.generation2</groupId>
 <artifactId>android-maven-plugin</artifactId>
 <version>${android.plugin.version}</version>
 <extensions>true</extensions>
 </plugin>
 </plugins>
 </pluginManagement>
 <plugins>
 <plugin>
 <groupId>com.jayway.maven.plugins.android.generation2</groupId>
 <artifactId>android-maven-plugin</artifactId>
 <configuration>
 <sdk>
 <platform>17</platform>
 </sdk>
 </configuration>
 </plugin>
 </plugins>
 </build>

터미널을 열고 vi .bash_profile을 입력후 다음과 같이 편집
export ANDROID_NDK_ROOT=/Users/duongame/devtools/android-ndk
export ANDROID_SDK_ROOT=/Users/duongame/devtools/android-sdk
export ANDROID_HOME=$ANDROID_SDK_ROOT
export NDK_ROOT=$ANDROID_NDK_ROOT
export MAVEN_HOME=/Users/duongame/devtools/maven
export PATH=$PATH:$ANDROID_NDK_ROOT:$MAVEN_HOME/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools

터미널을 닫은후에 다시 열고(.bash_profile 적용을 위해) 해당 프로젝트의 폴더로 이동후에 mvn clean install을 입력 -> target 폴더안에 apk파일이 생성됨
단말로 전송
단말로 복사: mvn android:deploy
단말로 복사후 실행: mvn android:run

참고 자료:
Android Maven으로 개발환경 구성하기: http://krespo.net/?p=220
Android archetypes: https://github.com/akquinet/android-archetypes
Maven android plugin samples: https://github.com/jayway/maven-android-plugin-samples/blob/master/support4demos/pom.xml

Android 키보드 입력 IME 예제

EditBox 없이 키보드 입력을 받아서 IME를 제작하는 예제

package com.example.keyboardsample;

import android.content.Context;
import android.text.Editable;
import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;

public class WordComposer extends View {
 private EditableInputConnection mEii;

 public WordComposer(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
  setFocusableInTouchMode(true);
  setFocusable(true);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  super.onTouchEvent(event);
  if (event.getAction() == MotionEvent.ACTION_UP) {

   InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
   imm.showSoftInput(this, InputMethodManager.SHOW_FORCED);
  }
  return true;
 }

 @Override
 public boolean onCheckIsTextEditor() {
  return true;
 }

 @Override
 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {

  outAttrs.actionLabel = null;
  outAttrs.label = "Composer Test";
  outAttrs.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
  outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
  if (mEii == null)
   mEii = new EditableInputConnection(this);
  return mEii;
 }

 public class EditableInputConnection extends BaseInputConnection {
  private final View mView;
  private SpannableStringBuilder mEditable;
  String mText = new String();

  public EditableInputConnection(View textview) {
   super(textview, true);
   mView = textview;
   mEditable = (SpannableStringBuilder) Editable.Factory.getInstance().newEditable("composer");
  }

  public Editable getEditable() {
   return mEditable;
  }

  /*이곳을 통해 영문입력을 전달 받으세요. for English*/
  @Override
  public boolean commitText(CharSequence text, int newCursorPosition) {
   Log.d("WordComposer", text.toString());
   return super.commitText(text, newCursorPosition);
  }

  /*이곳을 통해 한글 입력을 바로바로 전달 받으세요. for Multibyte code*/
  @Override
  public boolean setComposingText(CharSequence text, int newCursorPosition) {
   Log.d("WordComposer", text.toString());
   return super.setComposingText(text, newCursorPosition);
  }
 }
}

Android SurfaceView 예제

package com.example.surfaceviewsample;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // 이걸 써주면 타이틀바가 잠시 나왔다가 사라진다.
  requestWindowFeature(Window.FEATURE_NO_TITLE);

  CustomView view = new CustomView(this);
  view.getHolder().setFormat(PixelFormat.TRANSLUCENT);     //서피스뷰 투명 배경
  setContentView(view);

  //setContentView(R.layout.activity_main);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 class CustomView extends SurfaceView implements SurfaceHolder.Callback {
  private CustomViewThread cvt;
  Bitmap bm;

  public CustomView(Context context) {
   super(context);
   getHolder().addCallback(this);
   cvt = new CustomViewThread(getHolder(), this);
   bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
  }

  @Override
  public void onDraw(Canvas canvas) {
   //canvas.drawBitmap(bm, 64, 64, null);
   // 여기서는 안그려짐.
//   Paint paint = new Paint();
//   paint.setColor(Color.WHITE);
//   canvas.drawCircle(100, 100, 50, paint);

  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
   // TODO Auto-generated method stub

  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
   cvt.setRunning(true);
   cvt.start();
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {

   boolean retry = true;
   cvt.setRunning(false);
   while (retry) {
    try {
     cvt.join();
     retry = false;
    } catch (InterruptedException e) {

    }
   }
  }
 }

 class CustomViewThread extends Thread {
  private SurfaceHolder surfaceholder;
  private CustomView customview;
  private boolean running = false;

  public CustomViewThread(SurfaceHolder surfaceHolder, CustomView CustomView) {
   surfaceholder = surfaceHolder;
   customview = CustomView;
  }

  public void setRunning(boolean run) {
   running = run;
  }

  @Override
  public void run() {
   Canvas c;
   while (running) {
    c = null;
    try {

     c = surfaceholder.lockCanvas(null);
     synchronized (surfaceholder) {
      // onDraw는 android-18에서는 호출이 안되게 막혀있다.
//      customview.onDraw(c);
      // draw는 call해서 onDraw가 불리게 할수는 있지만 onDraw에서 그리면 안그려진다.
//      customview.draw(c);
   
      // 여기서 직접 그려야 한다.
      Paint paint = new Paint();
      paint.setColor(Color.WHITE);
      c.drawCircle(100, 100, 50, paint);
   
      // 아이콘 그리기.
      c.drawBitmap(customview.bm, 64, 64, null);

     }
    } finally {

     if (c != null) {
      surfaceholder.unlockCanvasAndPost(c);
     }
    }
   }
  }
 }

}

Android SMS 받기 예제

manifest에 다음과 같은 퍼미션 추가
    <uses-permission android:name="android.permission.RECEIVE_SMS" />


다음과 같이 resume에서 SMS 받기를 등록하고, pause에서 SMS 받기를 해제한다.
 protected void onResume() {
  super.onResume();
  registerReceiver(mReceiverBR, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
 }

 protected void onPause() {
  super.onPause();
  unregisterReceiver(mReceiverBR);
 }

 BroadcastReceiver mReceiverBR = new BroadcastReceiver() {
  public void onReceive(Context context, Intent intent) {
   String result = "";
   Bundle bundle = intent.getExtras();
   if (bundle != null) {
    Object[] pdus = (Object[]) bundle.get("pdus");
    for (int i = 0; i < pdus.length; i++) {
     SmsMessage msg = SmsMessage.createFromPdu((byte[]) pdus[i]);
     result += "from " + msg.getOriginatingAddress() + " => " + msg.getMessageBody() + "\n";
    }
    mResult.setText("메시지 : " + result);
   }
  }

 };

Android 상태바와 타이틀바의 높이 측정

parent가 전체 화면을 차지 하는 View일 경우

int[] location = new int[2];
parent.getLocationOnScreen(location);
mStatusAndTitleBarHeight = location[1];

Android 상태바와 타이틀바 숨기기

안드로이드에서 전체화면을 사용하기 위해서는 상태바(Status Bar)와 타이틀바(Title Bar)를 숨겨야 합니다. 숨기는 방법은 여러가지가 존재하는데 그 중 몇가지 방법을 정리하도록 하겠습니다.

1. 미리 정의된 Theme 사용하기
1.
<activity android:name=".MyActivity"
2.
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"  >
AndroidManifest.xml 에서 Activity의 Theme를 위와 같이 설정해주면 Status Bar와 Title Bar가 모두 없는 상태가 됩니다.

1.
<activity android:name=".MyActivity"
2.
android:theme="@android:style/Theme.NoTitleBar"  >
이렇게만 한다면 TitleBar만 없는 상태가 됩니다.

2. 내가 정의한 Theme 에서 설정하기

1.
<item name="windowNoTitle">true</item>
Title Bar만 없는 상태로 만들기

1.
<item name="windowFullscreen">true</item>
Status Bar와 Title Bar 모두 없는 상태 만들기

3. Java Code에서 설정하기

1.
requestWindowFeature(Window.FEATURE_NO_TITLE);
Title Bar 없는 상태로 만들기

1.
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
2.
WindowManager.LayoutParams.FLAG_FULLSCREEN);
Status Bar 없는 상태로 만들기. Theme 설정과 다른 것은 Fullscreen Flag를 주더라도 Title Bar는 남아있습니다.

Android 내폰번호 구하기


안드로이드 내 핸드폰 번호 얻는 소스이다.
잘동작하는데 국내에서 주의할 사항은 SKT/LGT는 010****####으로 나오는데 KT는 +8210****####으로 나온다는 점이다.

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

package com.example.line1number;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  TelephonyManager tm = (TelephonyManager) this
    .getSystemService(Context.TELEPHONY_SERVICE);
  // Getting my phone number
  String myNumber = tm.getLine1Number();

  TextView model = (TextView) findViewById(R.id.phonenumber);
  model.setText("[" + myNumber + "] length=" + myNumber.length());

 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

}

Saturday, September 13, 2014

C# ini 파일 사용하기

//DllImport ini
using System.Runtime.InteropServices;

//StringBuilder
using System.Text;

//DllImport ini
[DllImport("kernel32")]

private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);

[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

StringBuilder ret = new StringBuilder (256);
GetPrivateProfileString("Title", "Key", "Defualt", ret, 256, filepath);

GetPrivateProfileString에서 StringBuilder를 사용하는 이유는 return값이 write되는 char[]형이기 때문이다.

iOS ipa 파일 수동으로 설치하기

아이폰으로 archive된 ipa파일을 수동으로 설치하는 방법이다.
1. ipa파일을 만든다.
2. iTunes를 열고 ipa파일을 Finder에서 더블클릭한다.
3. 설치를 진행한다.




iOS Facebook SDK Email 구하기

페이스북 로그인 구현시 Email을 페이스북에서 제공하는 FBLoginView를 사용하지 않고 CustomUI 또는 프로그램으로 자동 로그인을 구현할때는 다음과 같은 방법을 사용한다.

프로그램 기본 소스는 FBLoginCustomUISample을 기반으로 사용한다.
https://github.com/fbsamples/ios-howtos.git

CustomLoginViewController.m
@implementation CustomLoginViewController

- (IBAction)buttonTouched:(id)sender
{
  // If the session state is any of the two "open" states when the button is clicked
  if (FBSession.activeSession.state == FBSessionStateOpen
      || FBSession.activeSession.state == FBSessionStateOpenTokenExtended) {

    // Close the session and remove the access token from the cache
    // The session state handler (in the app delegate) will be called automatically
    [FBSession.activeSession closeAndClearTokenInformation];
 
  // If the session state is not any of the two "open" states when the button is clicked
  } else {
    // Open a session showing the user the login UI
    // You must ALWAYS ask for public_profile permissions when opening a session
    [FBSession openActiveSessionWithReadPermissions:@[@"email"]
                                       allowLoginUI:YES
                                  completionHandler:
     ^(FBSession *session, FBSessionState state, NSError *error) {

       // Retrieve the app delegate
       AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
       // Call the app delegate's sessionStateChanged:state:error method to handle session state changes
       [appDelegate sessionStateChanged:session state:state error:error];
     }];
  }
}

AppDelegate.m
// This method will handle ALL the session state changes in the app
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
  // If the session was opened successfully
  if (!error && state == FBSessionStateOpen){
    NSLog(@"Session opened");
    // Show the user the logged-in UI
    [self userLoggedIn];
//      [user objectForKey:@"email"];
      [[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
          if (error) {
              //error
          } else {
              NSString *email = [user objectForKey:@"email"];
              NSLog(@"email=%@", email);
//              self.myFirstNameLabel.text = user.first_name;
//              self.myLastNameLabel.text = user.last_name;
              // self.myEmailLabel.text = @"";
          }
      }];
    return;
  }

iOS AVAudioPlayer 재시작하기

AVAudioPlayer에서 이미 생성되고 있는 사운드 재시작하는 방법이다.

//Pause the player and restart it
if (player.playing) {
    NSLog(@"Reset sound: %@", selectedSound);
    [player pause];
}

player.currentTime = 0;
[player play];

iOS AVAudioPlayer mp3 재생

NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:@"mp3"];
NSURL *url = [NSURL fileURLWithPath:path];
NSError *error; 
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
[audioPlayer play];

Friday, September 12, 2014

Android Native Support 제거하기

안드로이드에서 cpp파일을 자동으로 빌드하기 위해서 native support를 add하면 자동으로 ndk-build가 활성화 된다. 그런데 빌드할 cpp가 없을 때에는 native support를 add하면 항상 ndk-build 에러가 나서 개발을 진행할 수가 없다. 그럴 때는 다음과 같이 하면 native support가 제거 된다.

1. 프로젝트 root폴더(.project)가 있는 폴더에서 .cproject를 삭제 한다.
2. workspace에서 프로젝트를 delete했다가 재 import한다.

iOS 풀스크린 상태바 없애기

@implementation ViewController

- (BOOL)prefersStatusBarHidden {
    return YES; 
}

iOS 강제로 가로모드 만들기

@implementation ViewController

- (BOOL)shouldAutorotate {
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationLandscapeRight;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

iOS clock_gettime() 에러 해결

iOS에서는 리눅스의 clock_gettime()함수가 존재하지 않는다.
따라서 리눅스 코드를 포팅할때는 아래와 같은 대체함수를 사용하여야 한다.

#include <time.h>
#include <sys/time.h>

#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif

struct timespec ts;

#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
ts.tv_sec = mts.tv_sec;
ts.tv_nsec = mts.tv_nsec;

#else
clock_gettime(CLOCK_REALTIME, &ts);
#endif

iOS 파일 읽기 쓰기

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    
    NSFileManager * fileManager = [NSFileManager defaultManager];
    NSString *currentPath = [fileManager currentDirectoryPath];
    
    NSArray *dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentPath = [dirPath objectAtIndex:0];

    NSLog(@"currentPath %@", currentPath);
    NSLog(@"documentPath %@", documentPath);
    
    if ([fileManager changeCurrentDirectoryPath:documentPath] == NO){
        // 폴더 이동 못함
    } else {
        // 폴더 이동함
        NSLog(@"폴더 이동");
    }
    
    char *bytes = "1234";
    NSData *data = [[NSData alloc]initWithBytes:bytes length:4];
    [fileManager createFileAtPath:@"test.txt" contents:data attributes:nil]; 
}

iOS 레티나 시뮬레이터 해상도 문제

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

에서 [window frame]을 얻었을 경우 width=320, height=480이 나온다.
그런데 실제 레티나 3.5인치의 해상도는 640, 960이다.

이렇게 나온 이유는 frame의 rect값이 pixel이 아닌 point인데에 있다.
그래서 [[UIScreen mainScreen]scale]을 얻어서 곱해 줘야 한다는 것이다.
레티나에서는 2.0이 비-레티나에서는 1.0이 나온다.

iOS 함수 다수의 파라미터 정의하기

다음과 같이 C++로 정의 되는 함수를 Objective-C로 바꾸면 다음과 같다.

void setPersonData( char* name, int age, float height ) {

여기서 andAge, andHeight는 사용되지 않으며 이름을 변경하여도 상관없다.
그리고 유의할 사항은 ,가 아니라 스페이스로 구분한다는 점이다.

- (void) setPersonName: (char *)name andAge:(int)age andHeight:(float)height {

iOS UIImage에 CoreGraphics로 그리기

UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:drawImage];

UIGraphicsBeginImageContext(drawImage.frame.size);

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];

CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), size);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), r, g, b, a);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y); CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y); CGContextStrokePath(UIGraphicsGetCurrentContext());

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

iOS CustomView에 메모리비트맵 그리기

#import <UIKit/UIKit.h>

@interface RenderView : UIView
{
    CGImageRef imageRef;
    char *rawData;
int rb, w, h;
    CGColorSpaceRef colorSpace;
}

@end

#import "RenderView.h"

@implementation RenderView

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    NSLog(@"initWithCoder");
    
    colorSpace = CGColorSpaceCreateDeviceRGB();
    
    w = self.bounds.size.width;
    h = self.bounds.size.height;
    rb = 4*w;
    
    NSLog(@"w=%i h=%i", w, h);
    rawData = (char*)malloc(h * rb);
    for(int i=0; i<h; i++) {
        for(int j=0; j<w; j++) {
            rawData[rb*i + 4*j+0] = 255;//A
            rawData[rb*i + 4*j+1] = 0;
            rawData[rb*i + 4*j+2] = 0;
            rawData[rb*i + 4*j+3] = 255;//R
        }
    }
    return self;
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    // Drawing code
    NSLog(@"RenderView.drawRect");
    
    // 빨간 사각형을 그려보자
    CGContextRef context = UIGraphicsGetCurrentContext();
  

    // 픽셀을 입력하자
    CGContextRef ctx = CGBitmapContextCreate(rawData,w,h, 8,
                                             rb,
                                             colorSpace,
                                             kCGImageAlphaPremultipliedLast
                                             );
    imageRef = CGBitmapContextCreateImage(ctx);
    CGContextRelease(ctx);
    
    // 그리자 이제
    CGContextDrawImage(context, CGRectMake(0,0,w,h), imageRef);
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    
}

@end

iOS 게임 렌더링 루프 만들기

// 선언
@interface RenderView : UIView 
{
    CADisplayLink *displayLink;
}

// 초기화 
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(runLoop)]; 
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

// 루프함수
-(void)runLoop {
    NSLog(@"runLoop");
    [self setNeedsDisplay];
}

SONY SonicStage 4.3 풀버전 다운로드

SONY MP3 플레이어를 위한 SonicStage의 최신버전인 4.3이다.

인터넷에서 추가 파일 다운로드 받지 않아도 되는 버전입니다. 소닉스테이지의 경우 영문버전 또는 한글버전을 영문윈도우에 사용하면 노래제목의 ㄱ,ㄴ,ㄷ한글 정렬이 소니MP3상에서 되지 않아서 이 버전을 사용하는게 좋습니다.

저는 그래서 항상 이 버전만의 소닉스테이지를 사용합니다. 호환성이 제일 좋은거 같아요. 다른것은 음악이 인코딩이 제대로 안되거나 깨집니다.

DirectX SDK 7.0 다운로드

DirectDraw 예제가 포함된 마지막 DirectX SDK 버전인 7.0이다.

NVIDIA SDK 5.21 풀버전 다운로드

NVIDIA의 D3D 8.0, Vertex&Pixel Shader 1.1, 1.3버전의 최종 SDK이다.(GeForce3/4 대상) 이후 nVidia SDK 6버전 이상은 D3D 9.0이상 버전으로 구하기가 쉽지 않다.

또한, OpenGL의 고전 Shader를 해석하기 위해서는 반드시 필요하다. 여기서 OpenGL 고전 Shader란 Vertex Program, Texture Shader, Register Combiner를 말한다.

Pixel Shader라고 알려진 Fragment Shader는 VS2.0모델부터 존재하며(PS2.0), Register Combiner 1,2,3가 각각 Pixel Shader 1.1, 1.2, 1.3이다.

고전 게임 개발자료 또는 그래픽스 자료를 수집하는 분에게 권한다.

Thursday, September 11, 2014

XAMPP를 설치후에 localhost가 제대로 안나올때

XAMPP를 설치후에 localhost를 입력하면 localhost/xampp/로 이동한다.
이렇게 동작하는 이유는 index.php안에 다음과 같이 redirect 명령이 들어 있기 때문이다.

<?php
if (!empty($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'])) {
$uri = 'https://';
} else {
$uri = 'http://';
}
$uri .= $_SERVER['HTTP_HOST'];
header('Location: '.$uri.'/xampp/');
exit;
?>
Something is wrong with the XAMPP installation :-(

그래서 c:\xampp\htdocs\index.php를 이름 index1.php로 변경하면 index.html이 정상적으로 로딩된다.

C# 소스코드 클릭시 디자이너 열리지 않게 하기

C#을 편집하다보면 클래스가 System.ComponentModel.Component를 상속받을때가 있는데 이때는 이 클래스가 UI 콤포넌트라고 인식하여 소스코드를 더블클릭하면 View Code모드의 편집기가 아닌 View Designer의 디자이너가 열릴때가 있다.

그런데 사실 UI 콤포넌트가 아니거나 UI 콤포넌트라고 하더라도 Code보기를 기본으로 보고 싶을때가 있다. 그럴 경우에는 다음과 같이 클래스의 위에다가 다음과 같은 어트리뷰트를 넣으면 된다. 그러면 다음부터는 이 소스파일을 클릭하면 소스코드(View Code)가 열린다. 그리고 소스코드에 대한 아이콘도 일반 C# 소스처럼 바뀐다.

namespace TestNamespace
{
    [System.ComponentModel.DesignerCategory("Code")]
    public class TestClass : Component
    {
    }
}

Python 커맨드라인 아규먼트 받기

#c와 마찬가지로 argv이다. 0번은 *.py(실행소스이름)이 차지하고 있다.
import sys
print sys.argv[0]

#전체 모든 아규먼트를 출력
for arg in sys.argv:
 print arg

Python 디렉토리 순회 탐색하기

#이것도 os의 기능인가보다.
import os

#현재 디렉토리부터 모든 하위디렉토리의 디렉토리와 파일을 보여준다.
for dirname, dirnames, filenames in os.walk('.'):
 for subdirname in dirnames:
  name = os.path.join(dirname, subdirname)
  print name
 for filename in filenames:
  print os.path.join(dirname, filename)

Python 텍스트 파일 읽기

#c:\ProjectList.txt에서 모든 라인을 읽어서 화면에 출력하는 예제
ProjectList= 'c:\\ProjectList.txt'
fo = open(ProjectList, "r")
while 1:
    ln = fo.readline()
    if not ln:
        break
    print ln
fo.close()

Python 예외 처리하기

다음은 파일을 삭제할때 os에서 파일 삭제권한을 안주는 경우의 예외인 OSError를 잡는 경우이다.
pass는 C에서 아무것도 없는 {}과 같이 블럭에서 아무것도 안할 경우 써주는 문장이다.(함수에서 그냥 return과 유사)
import os
try:
  os.remove(filename)
except OSError:
  pass
print 'finished'

다음과 같이 없는 파일을 open하려고 하면 IOError가 발생한다.
else는 마지막인 final이라는 것과 같다.
try:
  fo = open('Makefile.trs', 'r')
except IOError:
  print '### IOError name: ' + name
  os.chdir(cwd)
  continue
else:
...

그리고 C#, Java와 같이 모든 예외를 처리하고자 할때는 다음과 같이 한다.
(catch(Exception e)와 같은 구문을 표현하려 할때)
except:
...

예외의 이름을 주고 싶을때는 다음과 같이 한다.
except WindowsError, we:

한번에 여러가지 예외를 잡고 싶을 때는 다음과 같이 괄호()를 한다.
except (OSError, IOError):

Python 개행문자 처리하기

#s가 문자열이라고 할경우
#파일에서 readline으로 읽은 경우 등 마지막에 \n을 달고 나오는 문자열이 있다.
#그럴때 다음과 같이 처리하면 끝문자를 한개 버릴수가 있다.
#s[:len(s)-1]는 tuple구문인데 처음부터 문자열 길이의 마지막에서 한개 적게 까지 선택하라는 명령이다.
#len은 문자열 길이를 구할수 있다. tuple길이도.

if s[len(s)-1] == '\n':
    s = s[:len(s)-1]

ASP 콘솔프로그램 실행 결과 받기

WScript.Shell 객체를 이용하면 외부 프로그램을 실행할 수가 있다.
그런데 외부 프로그램을 실행후 콘솔(커맨드)창의 실행 결과까지도 받을수가 있다.
주로 ASP에서 사용한다.(잘 동작한다.)

기존 객체 실행 프로그램은 wshell.run을 사용하는데 여기서는 wshell.exec를 사용하는것이 다르다.
exec를 사용하면 WshExecObject를 돌려주는데, 이 객체의 readall을 사용하면 결과 화면을 모두 얻어올 수 있다.

'객체 생성
dim wshell
set wshell = server.createobject("wscript.shell")
response.write cmd&"<br>"
'객체 생성 실패시 에러
if wshell is nothing then
 response.write "S201:WScript.Shell Initialization Failure"
end If

Set oExec = wshell.exec(cmd)
'끝날때 까지 기다림
Do While oExec.Status = 0
Loop

'출력 메세지를 화면 출력
output = oExec.stdout.readall
response.write output
'리턴 코드
ret = oExec.ExitCode
response.write "ret="&ret&"<br>"

'객체 소멸
set wshell=nothing

ASP 외부프로그램 실행

ASP에서 외부 프로그램을 실행하는 방법은 COM+을 만들어서 함수를 호출하던가 아니면 exe 프로그램을 만들어서 프로세스를 생성하는 방법이 있다. 그러나 윈도우 2008이나 호스팅업체에서 COM+을 이용을 하지 못할때 exe를 만들어서 사용하면 원하는 기능을 사용할 수 있다.

다음과 같이 WScript.Shell이라는 COM을 사용하면 exe를 호출할 수 있다. 리턴값을 받기 위해서는 ret=wshell.run(cmd, 0, true)할 때 마지막 parameter를 true로 주어야 한다. 리턴이 올때까지 기다린다는 뜻이다.

만약 호출하는 exe가 윈도우 프로그램이거나(윈도우 메세지 루프를 가져서 프로그램이 끝나지 않음) 또는 파일을 쓰는 (FILE* 등)프로그램이면 리턴이 되지 않고 멈춰버린다.

'절대패스로 지정해야함
exe = "C:\testweb\test.exe"
cmd = exe + "arg1 arg2 ..."

'객체 생성
dim wshell
set wshell = server.createobject("wscript.shell")
response.write cmd&"<br>"

'객체 생성 실패시 에러
if wshell is nothing then
    response.write "S201:WScript.Shell Initialization Failure"
end if

'외부 프로그램 실행후 리턴값 받아서 화면에 출력
ret=wshell.run(cmd, 0, true)
response.write ret

'객체 소멸
set wshell=nothing

Ubuntu MySQL API 설치

1. mysql 클라이언트 설치:
The program 'mysql' is currently not installed. You can install it by typing:
sudo apt-get install mysql-client-core-5.5

2. mysql 서버 설치:
sudo apt-get install mysql-server

3. mysql 라이브러리 설치:
sudo apt-get install libmysqlclient15-dev

CentOS MySQL API 설치

yum -y install mysql
yum -y install mysql-server
yum -y install mysql-devel

chgrp -R mysql /var/lib/mysql
chmod -R 770 /var/lib/mysql

service mysqld start

// libmysqlclient 설치
yum whatprovides "*libmysqlclient*"

PHP SOAP 구현

PHP로 SOAP을 구현하려면 PHP5가 있어야 된다.
PHP5에만 SOAP이 내장되어 있다.

그러나 PHP4에서 구현을 하려면 NuSOAP을 사용하면 된다.
그리고 PHP5도 동일한 방법을 사용하면 되기 때문에 NuSOAP으로 하는것이 편하다.

nusoap-0.7.3.zip
nusoap-docs-0.7.3.zip
Mail_Mime-1.4.0.gz

세개의 파일이 필요한데
Mime은 NuSOAP 0.7.3에서 바이너리를 SOAP으로 보낼때 쓴다.

첨부된 nusoap.zip을 받고
htdocs폴더에다 lib에 풀고
 // NuSOAP설정
 include_once('lib/nusoap.php');
 include_once('lib/nusoapmime.php');
 // WSDL설정
 $wsdl = 'http://url/WebService/Service?wsdl';
 // NuSOAP설정
 $client = new nusoap_client($wsdl, 'wsdl');
 $client->setEndpoint("'http://url/WebService/Service");
위와 같이 하면 함수를 호출할 수 있다.
반드시 endPoint를 설정해야 한다.

Visual C++ 2012 redistributable (arm/x86/x64) 다운로드

Visual C++ 2012 redistributable (arm/x86/x64)

다음 링크에서 설치파일을 다운로드 받은 후에 설치한다.

다운로드

Visual C++ 2010 redistributable (x86) 다운로드

Visual C++ 2010 redistributable (x86)

다음 링크에서 설치파일을 다운로드 받은 후에 설치한다.

다운로드

Visual C++ 2008 redistributable (x86) 다운로드

Visual C++ 2008 redistributable (x86)

다음 링크에서 설치파일을 다운로드 받은 후에 설치한다.

다운로드

Visual C++ 2003 redistributable (x86) 다운로드

Visual C++ 2003 redistributable (x86)

다음 링크에서 vcredist_x86_2003.zip 파일을 다운로드 받은 후에 C:\Windows\System32에 압축을 푼다.

윈도우 64비트의 경우는 C:\Windows\SysWOW64에 압축을 푼다.

다운로드

Wednesday, September 10, 2014

Ubuntu Apache PHP MySQL 설치

0. 서버 URL 업데이트
sudo apt-get update

1. 아파치 서버 설치
sudo apt-get install apache2

2. MySQL 인증 모듈 설치
sudo apt-get install libapache2-mod-auth-mysql

3. MySQL 서버/클라이언트 설치
sudo apt-get install mysql-server mysql-client

4. PHP 설치
sudo apt-get install php5 php5-cli curl memcached php5-curl php5-gd php5-memcache php5-mysql php-apc php5-xsl php5-imap libssh2-php libapache2-mod-php5 php5-gd php5-xmlrpc php5-intl

5. 아파치 서버 재시작
sudo /etc/init.d/apache2 restart

6. 작동 상태 확인 (Check to see if it works)
sudo netstat -tap | grep mysql

올바르게 작동된다면 "LISTEN" 이라는 문구가 뜹니다
그렇지 않다면, mysql을 재시작해주세요.

sudo /etc/init.d/mysql restart

7. 아파치 및 MySQL 디폴트 디렉토리
Apache configuration: /etc/apache2/apache2.conf
Apache root location: /etc/apache2/sites-available/default
MySQL configuration: /etc/mysql/my.cnf
Default Web root: /var/www

Ubuntu "sudo apt-get install apache2"수행시 "Not Found..." 에러 해결 방법

"sudo apt-get install apache2" 수행시 "Not Found..." 에러가 나는 경우는 다운로드 할 서버의 URL을 찾지 못할 때이다.

그럴 때는 "sudo apt-get update"과 같이 업데이트를 하여 서버의 URL들을 업데이트를 먼저한다.

그런 다음 "sudo apt-get install apache2"를 수행하면 아파치가 정상적으로 수행된다.


Ubuntu MySQL 한글 설정

우분투에서 기본적으로 한글이 설정 안되어 있어서 한글을 입력하면 깨진다.
사용한 우분투 버전은 12.04 64비트 버전이다.

MySQL 접속을 위해 다음과 같이 입력하자.
mysql -u root -p
[암호입력]
status 명령어를 입력하여 보면 다음과 같이 입력되어 있을 것이다.
이렇게 되어 있으면 한글이 안된다.
Server characterset: latin1
Db characterset: latin1
따라서 MySQL 설정을 편집해 줘야 하는데 설정파일은 /etc/mysql/my.cnf이다.
먼저 파일을 복사하고 시작하자.
sudo cp /etc/mysql/my.cnf /etc/mysql/my.cnf.org
sudo gedit(or vi) /etc/mysql/my.cnf
# client 부분밑에 추가
[client]
default-character-set = utf8
# mysqld 부분밑에 추가
[mysqld]
init_connect = SET collation_connection = utf8_general_ci
init_connect = SET NAMES utf8
character-set-server = utf8
collation-server = utf8_general_ci
# mysqldump 부분밑에 추가
[mysqldump]
default-character-set = utf8
# mysql 부분밑에 추가
[mysql]
default-character-set = utf8
이제 파일을 저장했으니 mysql을 재시작하자.
인터넷에 돌아다니는 자료를 보면 각 리눅스나 설정마다 다른데 우분투의 경우는 다음과 같이 해결했다.
sudo /etc/init.d/mysql restart
다시 mysql -u root -p를 해서 status에서 Server와 Db characterset이 utf8이 나오면 성공이다.

Ubuntu phpMyAdmin 설치

1. 설치하기
 sudo apt-get install phpmyadmin

2. 설정하기
 sudo vi /etc/apache2/apache2.conf

맨 마직막에 아래 구문 추가
 1. # Enable PhpMyAdmin
 2. Include /etc/phpmyadmin/apache.conf

Apache 재시작
 sudo /etc/init.d/apache2 restart

PhpMyAdmin 접속
 http://localhost/phpmyadmin

3. 삭제하기
 sudo apt-get remove phpmyadmin

Ubuntu Server Repository 변경

KT 클라우드에서 우분투를 임대를 했는데 서버 repository를 변경하지 않으면 업데이트가 되지 않았다.
그래서 다음과 같은 조치를 취했다.

1. Source List 수정
sudo vi /etc/apt/sources.list

2. 아래 내용 수정 (vi 치환 기능 사용하여 한번에 수정)
:%s/kr.archive.ubuntu.com/ftp.daum.net/g 또는 저장소가 us.archive.ubuntu.com

3. 기타 저장소
:%s/security.ubuntu.com/ftp.daum.net/g
:%s/extras.ubuntu.com/ftp.daum.net/g

4. 업데이트 & 업그레이드
sudo apt-get update
sudo apt-get upgrade

Wednesday, September 3, 2014

Visual C++ 2010 Up-to-date 안되고 계속 컴파일하는 문제 해결 방법

최근에 프로젝트를 VS2008에서 VS2010으로 바꾸었는데 VS2008에는 발생하지 않던 문제가 발생해서 곤욕을 치루었다.

그것은 소스와 오브젝트 파일이 최신이어서 up-to-date이 나와야 되는데 자꾸 out-of-date라고 하면서 빌드를 계속 하는 문제였다.

여러가지 방법으로 찾아보았는데 문제의 원인은 이것이었다.

https://stackoverflow.com/questions/6054066/vs2010-project-is-not-up-to-date-because-alwayscreate-was-specified/6054132#6054132

1. In my case in VS10 it was due to having missing (but non-complied .h files, thus no additional error to identify) in project folders.

A quick check that all project files can open in editor fixed this problem.

2. I had a similar problem when one of the include files listed in the project didn't actually exist. I had deleted the file, but forgot to remove it from the project.

The dependency checker then believes the project is not up to date, but the builder finds nothing to build.

두가지가 다 같은 답변인에 요약하자면, 프로젝트에 추가된 .h파일이 있는데 그것이 실제로 존재하지 않는 파일이 추가되어 있어서 발생한다라는 것이다.

해결책은 모든 프로젝트에 추가된 .h파일을 열어보고 없으면 프로젝트에서 삭제하면된다.

Visual C++ 2012 빌드한 프로그램이 윈도우XP에서 실행이 안될때

Visual Studio 2012 Update 1 다운로드 받으셔서 설치 하면 가능 할 것 같습니다.

설치 후 Property Pages의 Configuration Properties의 General을 클릭합니다.

많은 속성들중  Platform Toolset 에 Visual Studio 2012  Windows XP (v110_xp) 항목이 추가된 것을 확인하실 수 있습니다.

Visual C++ 2008 브레이크 포인트 활성화 안될때

Visual Studio 2008 C++에서 소스파일이 실행파일과 동일한데도 불구하고 브레이크 포인트가 활성화가 안될 때거 있다.

이럴때는 해당소스파일을 열어서 [File]-[Advanced Save Options]을 클릭한다음에 다음과 같이 Unicode (UTF-8 with signature) - Codepage 65001로 저장하자.


Visual Studio 2010 이상에서는 위의 문제가 해결되었다.

Visual C++ 2012 라인번호 표시하기


Visual Studio 2012 라인번호 표시는 기본적으로 꺼져있다.

그래서 [Tools]-[Options]-[Text Editor]-[C/C++]에서 Display-Line numbers를 체크 On하여 라인번호를 활성화 하면 된다.

Monday, September 1, 2014

Flex Module Compile

하나의 Flex 프로젝트에서 여러 개의 SWF 파일이 생성되는 경우가 있다.

이는 특정 MXML 파일을 Module이라고 해서 별도의 SWF 파일로 컴파일 한후에 그것을 Load Module하여 사용하는 것을 말한다. 윈도우의 Visual C++에서 DLL을 만드는 것과 유사하다.

다음과 같이 프로젝트 속성에서 Flex Modules 탭으로 간다음에 SWF로 생성할 MXML을 고르면 다음 빌드시 SWF파일이 만들어진다.