Wojny wsadowe – jesteś pewien?

Tym razem zajmiemy się wyświetlaniem okna komunikatu z poziomu skryptu powłoki Windows. Oknu możemy nadać te klawisze, które chcemy, np. Ok, Yes i No, czy jakikolwiek inny zestaw, a także na wyjściu sprawdzić, co user ewentualnie wybrał (jeśli jakiś wybór mu daliśmy 😉 ).

Jeśli chcemy użyć pełnego wachlarza możliwości, stwierdziłem, że najmniejszą linią oporu będzie posłużenie się skryptem VisualBasic i jego funkcją MsgBox, np.:

MsgBox("Are you sure you want to do this??",4,"Haha!")

Pierwszy parametr to treść komunikatu, trzeci to tytuł okna. Drugi parametr, to zestaw przycisków, które mają być udostępnione użytkownikowi do wyboru. Możliwości są następujące:

0 Tylko przycisk OK
1 OK i Cancel
2 Abort, Retry i Ignore
3 Yes, No i Cancel
4 Yes i No
5 Retry i Cancel
16 Użyj ikonki Critical Message
32 Użyj ikonki Warning Query
48 Użyj ikonki Warning Message
64 Użyj ikonki Information Message
0 Pierwszy przycisk jest domyślny
256 Drugi przycisk jest domyślny
512 Trzeci przycisk jest domyślny
768 Czwarty przycisk jest domyślny

Jak widać, mamy tu dostępne trzy grupy. Wybieramy pożądane opcje i sumujemy je, np. chcąc wyświetlić klawisze „Yes” i „No”, dodać ikonkę „Warning Query” i uczynić przycisk „No” domyślnie zaznaczonym, użyjemy wartości 4+32+256:

MsgBox("Are you sure you want to do this??",4+32+256,"Haha!")

Oczywiście, nic nie stoi na przeszkodzie, aby wpisać tam po prostu 292, ale dla późniejszej analizy i edycji, łatwiej pozostawić to w takiej czytelnej postaci.

Skrypt VBS skryptem VBS, ale my chcemy wywołać nasze okienko z regularnego skryptu wsadowego… uciekniemy się do małego tricku i… stworzymy tymczasowy plik vbs i go wywołamy 😉

echo MsgBox("Are you sure you want to do this??",4+32+256,"Haha!") >tempmsg.vbs
call tempmsg.vbs

Tu pojawia się kolejny problem: jak sprawdzić, co wybrał użytkownik? Najpierw, zmusimy nasz skrypt VBS do zwracania na swoim wyjściu wynik funkcji MsgBox – czyli właśnie wybór użytkownika. Robimy to tak:

wscript.quit MsgBox("Are you sure you want to do this??",4+32+256,"Haha!")

Dzięki temu, cały skrypt na swoim wyjściu zwróci wartość wg klucza:

1 OK
2 Cancel
3 Abort
4 Retry
5 Ignore
6 Yes
7 No

W naszym pierwotnym skrypcie, po wywołaniu skryptu VBS, musimy sprawdzić wartość zmiennej %errorlevel%, która wskazuje właśnie na wynik ostatnio wykonanej komendy.

echo wscript.quit MsgBox("Are you sure you want to do this??",4+32+256,"Haha!") >tempmsg.vbs
call tempmsg.vbs
if %errorlevel% NEQ 6 goto finish

echo Jest! Zgodził się! Hurrra!!
echo Tutaj rozkręcamy imprezkę...

:finish
del tempmsg.vbs

Prawda, że proste? 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Fullscreen widget w Qt4

Stworzenie widgeta pełnoekranowego w Qt4 niby jest proste, ale… okazuje się, że po drodze czyha na nas kilka pułapek. Postaram się was przez nie przeprowadzić…

W moim przypadku, mam aplikację, która wyświetla strumień video (akurat ze specjalnej karty, ale to kompletnie nieważne – może Ty chcesz oglądać np. film z pliku). Idąc za niepisanym standardem, postanowiłem opcję przejścia do fullscreen położyć na klawiszu <F11>.

QAction *theFullscreenAction = new QAction(this);
theFullscreenAction->setShortcut(QKeySequence(Qt::Key_F11));
theFullscreenAction->setCheckable(true);
connect(theFullscreenAction, SIGNAL(toggled(bool)), this, SLOT(toggleFullscreen(bool)));
addAction(theFullscreenAction);

Aby przełączyć widget, trzeba zapamiętać kilka jego właściwości, aby było do czego wracać a następnie go przełączyć. Przy powrocie, oczywiście należy odtworzyć wszystkie właściwości.

QWidget *videoFrameParent;
QLayout *videoFrameParentLayout;
Qt::WindowFlags videoFrameFlags;
QSize videoFrameSize;
void myApp::toggleFullscreen(bool bFullscreen)
	{
	if (bFullscreen)
		{
		videoFrameParent = ui.videoFrame->parentWidget();
		videoFrameParentLayout = ui.videoFrame->parentWidget()-&gt;layout();
		videoFrameFlags = ui.videoFrame->windowFlags();
		videoFrameSize = ui.videoFrame->size();

		// untie it...
		ui.videoFrame->setParent(NULL);
		ui.videoFrame->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
		ui.videoFrame->showMaximized();
		}
	else
		{
		// bring it back!
		ui.videoFrame->resize(videoFrameSize);
		ui.videoFrame->overrideWindowFlags(videoFrameFlags);
		ui.videoFrame->setParent(videoFrameParent);
		videoFrameParentLayout>addWidget(ui.videoFrame);
		ui.videoFrame->show();
		}
	}

Odpalamy aplikację i testujemy… pierwsze, co się rzuca, to nie działa klawisz powrotu. Chwila zabawy i – działa, ale trzeba Alt-Tabem przejść do osieroconego okna aplikacji głównej. Rozwiązanie jest dość proste: wystarczy naszą akcję dodać również do naszego widgetu. Dlaczego? Dlatego, że po jego wydziedziczeniu (setParent(NULL);) aplikacja główna przestaje otrzymywać zdarzenia klawiatury, gdy nasz widget ma fokus.

ui.videoFrame->addAction(theFullscreenAction);

Kolejny problem objawia się w momencie, gdy mamy konfigurację wielomonitorową i wywołamy akcję w momencie, gdy aplikacja będzie na monitorze innym, niż główny. Okaże się, że nasz fullscreen włączy się na ekranie głównym, pozostawiając osieroconą i ogołoconą aplikację główną tam, gdzie była.

Tu niestety musimy kilka linijek kodu spędzić na rozwiązanie tego problemu… sprawdzimy najpierw, gdzie nasza aplikacja się znajduje, a następnie (po wydziedziczeniu!) przesuniemy okno tam ręcznie, po czym dopiero uruchomimy showMaximized().

// untie it...
ui.videoFrame->setParent(NULL);
ui.videoFrame->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);

// hack for showing it on a screen, where the app is (in a multimonitor setup)
QDesktopWidget *dw = QApplication::desktop();
QRect drect = dw->screenGeometry(dw->screenNumber(QCursor::pos()));
int desk_x = drect.width();
int desk_y = drect.height();
int x = ui.videoFrame->width();
int y = ui.videoFrame->height();
ui.videoFrame->move(desk_x / 2 - x / 2 + drect.left(), desk_y / 2 - y / 2 + drect.top());

// yeah!
ui.videoFrame->showMaximized();

Kolejny test i… działa! 🙂 Poniżej kod w komplecie:

QWidget *videoFrameParent;
QLayout *videoFrameParentLayout;
Qt::WindowFlags videoFrameFlags;
QSize videoFrameSize;
QAction *theFullscreenAction = new QAction(this);
theFullscreenAction->setShortcut(QKeySequence(Qt::Key_F11));
theFullscreenAction->setCheckable(true);
connect(theFullscreenAction, SIGNAL(toggled(bool)), this, SLOT(toggleFullscreen(bool)));
addAction(theFullscreenAction);
ui.videoFrame->addAction(theFullscreenAction);
void myApp::toggleFullscreen(bool bFullscreen)
	{
	if (bFullscreen)
		{
		videoFrameParent = ui.videoFrame->parentWidget();
		videoFrameParentLayout = ui.videoFrame->parentWidget()->layout();
		videoFrameFlags = ui.videoFrame->windowFlags();
		videoFrameSize = ui.videoFrame->size();

		// untie it...
		ui.videoFrame->setParent(NULL);
		ui.videoFrame->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);

		// hack for showing it on a screen, where the app is (in a multimonitor setup)
		QDesktopWidget *dw = QApplication::desktop();
		QRect drect = dw->screenGeometry(dw->screenNumber(QCursor::pos()));
		int desk_x = drect.width();
		int desk_y = drect.height();
		int x = ui.videoFrame->width();
		int y = ui.videoFrame->height();
		ui.videoFrame->move(desk_x / 2 - x / 2 + drect.left(), desk_y / 2 - y / 2 + drect.top());

		// yeah!
		ui.videoFrame->showMaximized();
		}
	else
		{
		// bring it back!
		ui.videoFrame->resize(videoFrameSize);
		ui.videoFrame->overrideWindowFlags(videoFrameFlags);
		ui.videoFrame->setParent(videoFrameParent);
		videoFrameParentLayout->addWidget(ui.videoFrame);
		ui.videoFrame->show();
		}
	}

Miłego oglądania! 😉

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Android: pełna obsługa multitouch w grze

Dodać do programu (gry) obsługę dotyku pod Androidem jest niezmiernie prosto. Wystarczy kilka linijek kodu:

 

@Override
public boolean onTouchEvent(MotionEvent event)
{
	float x = event.getX();
	float y = event.getY();

	switch (event.getAction())
	{
	case MotionEvent.ACTION_DOWN:
		processTouchDown(x, y);
		break;
	case MotionEvent.ACTION_UP:
		processTouchUp(x, y);
		break;
	}		
	return false;
}

 

Schody zaczynają się w momencie, gdy chcemy zrobić np. pełne sterowanie postacią – a jego ramach np. ruch oraz strzelanie. Już przy pierwszym teście dochodzimy do wniosku, że chcemy obsługiwać wiele palców naraz, niezależnych od siebie.

 

Z pomocą przychodzi nam mechanizm dodany w API Level 8, czyli od Androida 2.2.x Froyo. Każdy event dotyku zawiera w sobie pełną informację o wszystkich ewentualnych punktach nacisku, ich pozycji itd. Dlatego, naszą obsługę zdarzeń rozwiniemy tak:

 

private int actionMovementId = -1;
private int actionFiringId = -1;

 

@Override
public boolean onTouchEvent(MotionEvent event)
{
	int actionId = event.getActionIndex();
	float x = event.getX(actionId);
	float y = event.getY(actionId);

	switch (event.getActionMasked())
	{
	case MotionEvent.ACTION_DOWN:
	case MotionEvent.ACTION_POINTER_DOWN:
		processTouchDown(x, y, actionId);
		break;
	case MotionEvent.ACTION_UP:
	case MotionEvent.ACTION_POINTER_UP:
		processTouchUp(x, y, actionId);
		break;
	}

	return false;
}

 

private void processTouchDown(float x, float y, int actionId)
{
	Point displaySize = new Point(CgEngine.display.getWidth(), CgEngine.display.getHeight());
	int touchableArea = displaySize.y * 3 / 4;
	int halfX = displaySize.x / 2;

	if (y &gt; touchableArea)
	{
		// fire!
		if (x &lt; halfX)
		{
			CgEngine.playerFireAction = CgEngine.PLAYER_FIRE_SHOOT;
			actionFiringId = actionId;
		}
		// steering
		else
		{
			if (x &lt; halfX * 1.5)
				CgEngine.playerRunAction = CgEngine.PLAYER_TURN_LEFT_1;
			else
				CgEngine.playerRunAction = CgEngine.PLAYER_TURN_RIGHT_1;

			actionMovementId = actionId;
		}
	}
}

 

private void processTouchUp(float x, float y, int actionId)
{
	if (actionFiringId == actionId)
	{
		CgEngine.playerFireAction = CgEngine.PLAYER_FIRE_HOLD;
		actionFiringId = -1;
	}
	if (actionMovementId == actionId)
	{
		CgEngine.playerRunAction = CgEngine.PLAYER_RELEASE;
		actionMovementId = -1;
	}
}

 

Jak widać, przy rozpoczynaniu każdej akcji, musimy zapamiętać identyfikator palca, który ją uruchomił – później, gdy przyjdzie event puszczenia palca, będzie można łatwo zidentyfikować, która akcja powinna się zakończyć.

 

Powodzenia!

 

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

MEncoder – tips&tricks

Od lat używam mencodera – a jednak wciąż potrafi zaskakiwać i zmuszać do ruszania głową… 😉 dlatego postanowiłem zgromadzić w jednym miejscu garść porad (również dla samego siebie).

 

Kodowanie H.264 szybkie:

-ovc x264 -x264encopts subq=4:bframes=2:b_pyramid=normal:weight_b

 

Kodowanie H.264 wysoka jakość:

-ovc x264 -x264encopts subq=5:8x8dct:frameref=2:bframes=3:b_pyramid=normal:weight_b

 

Kodowanie H.264 bardzo wysoka jakość:

-ovc x264 -x264encopts subq=6:partitions=all:8x8dct:me=umh:frameref=5:bframes=3:b_pyramid=normal:weight_

 

Zmiana prędkości poprzez zmianę framerate’u:

-speed 8

Trzeba zauważyć, że ten efekt jest raczej słaby i niewiele jest prawdziwych zastosowań dla niego…

 

Zmiana prędkości poprzez odrzucenie ramek (tu 8x):

-fps 240 -ofps 30

A tu mała sztuczka, pozwalająca na prawdziwe przyśpieszenie filmu 🙂 MEncoder przy takiej konstrukcji zacznie odrzucać zbędne ramki.

 

Odwrócenie filmu do góry nogami (np. w nagraniu z telefonu):

-vf flip,mirror

 

Odwrócenie filmu w prawo:

-vf rotate=1

Odwrócenie filmu w lewo:

-vf rotate=2

 

Bez dźwięku:

-nosound

Podłożenie zewnętrznej ścieżki audio:

-audiofile sciezka.mp3 -oac mp3lame -lameopts mode=2:cbr:br=128:vol=0

Kodowanie MP3:

-oac mp3lame -lameopts mode=2:cbr:br=128:vol=0

Zrzut pliku MP3 z filmu:

mencoder film.avi -oac copy -of rawaudio -ovc copy -o sameaudio.mp3

 

Przycięcie filmu:

-ss 0:00:35 -endpos 0:07:47

Połączenie kilku plików:

mencoder plik1.mp4 plik2.mp4 plik3.mp4 -ovc copy -oac pcm -o joined.mp4

 

 

I na koniec mały przykład, który właśnie przed chwilą uskuteczniałem: odwrócenie filmu do góry nogami, 8-krotne przyśpieszenie, podłożenie ścieżki audio z MP3 i kodowanie całości do H.264:

mencoder filmik.mp4 -vf flip,mirror -ovc x264 -x264encopts subq=5:8x8dct:frameref=2:bframes=3:b_pyramid=normal:weight_b -fps 240 -ofps 30 -audiofile muza.mp3 -oac mp3lame -lameopts mode=2:cbr:br=128:vol=0 -o final_video.mp4

 

Miłego kodowania! 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Wojny wsadowe – wysyłamy maila

Wojen wsadowych ciąg dalszy…

Dzisiaj zachciało mi się dodać do mojego skryptu nocnych buildów aplikacji wysyłanie mailem loga z owego buildu. Czystym batchem nie udało mi się tego osiągnąć, ale na szczęście już od dość dawna pod Windowsem działa zarówno JavaScript jak i VBScript, który ma dostęp do kilku ciekawych obiektów systemowych. Jednym z takich obiektów jest CDO.Message.

Tworzymy najpierw skrypt VB, który będzie wysyłał maila za pośrednictwem GMail:

Set objMessage = CreateObject("CDO.Message")
objMessage.From = "builder@firma.pl" 
objMessage.To = WScript.Arguments.Item(0)
builtPackage = WScript.Arguments.Item(2)
objMessage.Subject = "Nightly build report for " & builtPackage
objMessage.TextBody = builtPackage & " nightly build results in attachement." 
If WScript.Arguments.Count > 3 Then objMessage.AddAttachment WScript.Arguments.Item(3) End If

schema = "http://schemas.microsoft.com/cdo/configuration/"
Set msgConf = objMessage.Configuration
msgConf.Fields.Item (schema & "sendusing") = 2
msgConf.Fields.Item (schema & "smtpserver") = "smtp.gmail.com"
msgConf.Fields.Item (schema & "smtpserverport") = 465
msgConf.Fields.Item (schema & "smtpauthenticate") = 1
msgConf.Fields.Item (schema & "sendusername") = WScript.Arguments.Item(0)
msgConf.Fields.Item (schema & "sendpassword") = WScript.Arguments.Item(1)
msgConf.Fields.Item (schema & "smtpusessl") = true
msgConf.Fields.Update

objMessage.Send()

Przyjmuje on trzy wymagane parametry i czwarty opcjonalnie. Pierwsze dwa to user i hasło do konta GMail, trzeci to tekst użyty w temacie i body. Czwarty parametr to plik, który zostanie dołączony jako załącznik do maila. Uwaga: ścieżka do pliku musi być bezwzględna.

Do swojego skryptu budującego dodałem takie cuś:

if "%MAIL_USER%" NEQ "" (
if "%MAIL_PASSWD%" NEQ "" (
sendmail.vbs %MAIL_USER% %MAIL_PASSWD% %PRODUCT_NAME%_%PRODUCT_MAJOR%.%PRODUCT_MINOR% %BUILD_LOG%
)
)

Sprawdza on, czy zdefiniowane są zmienne środowiskowe MAIL_USER i MAIL_PASSWD i jeśli tak, to wysyła maila.

🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Wojny wsadowe

…czyli próba napisania skryptu w Windowsowym batchu…

Confused

Sprawa nie jest prosta, jak wszyscy wiedzą, ale… możliwa, o czym już nie wszyscy wiedzą. O wyszukiwaniu stringów już kiedyś pisałem, dzisiaj porcja malutkich hintów, przydatnych w każdym chyba dużym skrypcie.

Wczytanie zawartości pliku do zmiennej.

Mam sobie plik o nazwie build_branch, a w nim zapisaną nazwę brancha, który będę budował. Chcę tę nazwę wpisać do zmiennej:

set /p BUILD_BRANCH= < build_branch

i… już Smile można używać zmiennej %BUILD_BRANCH%.

Zmiana znaków w stringu.

Okazuje się to również banalnie proste! Np. usunięcie cudzysłowiów:

set STRING2=%STRING:"=%

albo zamiana spacji na zera (np. w godzinie):

set TIMESTAMP=%TIMESTAMP_TEMP: =0%

prawda, że proste?

 

Plik z timestampem w nazwie.

Tu z pomocą przyjdzie nam wybieranie subłańcuchów ze zmiennych systemowych %DATE% i %TIME%:

set TIMESTAMP_TEMP=%date:~0,4%%date:~5,2%%date:~8,2%-%time:~0,2%%time:~3,2%
set TIMESTAMP=%TIMESTAMP_TEMP: =0%

Druga linia potrzebna jest, bo możemy otrzymać spacje w godzinach 1-9.

 

Odwołanie się do ścieżki, skąd był wywołany skrypt.

W skryptach często zmieniamy katalog bieżący, stąd, aby wywołać jakiś inny skrypt, bądź użyć pliku, musimy zrobić to tak:

set BUILD_LOG=%~dp0\build_MVP_log-%TIMESTAMP%.txt

Sformułowanie %~dp0 wskazuje na katalog, w którym znajduje się oryginalnie wołany skrypt.

 

Odczyt wartości z bardziej złożonego pliku.

Mam na dysku plik build_ID.hpp o treści:

#define DG_BUILD_NUMBER /*CFGMGMT_BUILD_NUMBER*/ 1300
#define DG_BUILD_STAMP /*CFGMGMT_BUILD_STAMP*/ "Tvrc315_2013_0403_084553"

Potrzebuję wyciągnąć wartości 1300Tvrc315_2013_0403_084553 do zmiennych.

findstr DG_BUILD_NUMBER %sandbox%\buildID\build_ID.hpp > DG_BUILD_NUMBER.tmp
set /p STRING= < DG_BUILD_NUMBER.tmp
del DG_BUILD_NUMBER.tmp
findstr DG_BUILD_STAMP %sandbox%\buildID\build_ID.hpp > DG_BUILD_STAMP.tmp
set /p STRING= < DG_BUILD_STAMP.tmp
del DG_BUILD_STAMP.tmp

To daje nam pojedyncze pożądane linie (załóżmy, że w pliku były jeszcze inne treści). Pozostaje nam odczytać same wartości. Tu skorzystamy z faktu, że to, co nas interesuje, jest zawsze na końcu linii:

for %%I in (%STRING%) do set BUILD_NUMER=%%I
for %%J in (%STRING2%) do set BUILD_TAG=%%J

i już mamy zmienne %BUILD_NUMER% i %BUILD_TAG%, wczytane z pliku Smile

 

Reasumując: nie jest to oczywiście Bash, ale da się coś tam w tym zrobić… jest to co prawda upierdliwe – ale się da.

Big Boss

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Win32 + gcc 4.7 + Qt 4.8

Gcc, którego używam pod Windowsami, to mingw w wersji tdm (http://tdm-gcc.tdragon.net/). Aby skompilować pod niego Qt4 (target na x86), trzeba chwilkę na to poświęcić… oto, jak ja sobie z tym radzę:

Gcc mam w katalogu C:\gcc\gcc-4.7.1-tdm. Ogólnie, w systemie mam jeszcze kilka innych kompilatorów, na co będzie trzeba zwrócić za chwilę uwagę, zmieniając zmienne środowiskowe INCLUDE i LIB, bo inaczej zrobi się lekki burdel…

Zakładam katalog C:\Qt\4.8.3, w nim rozpakowuję źródła Qt (podkatalog qt-everywhere-opensource-src-4.8.3) oraz zakładam katalog docelowy, do którego wykonam builda (mingw32). Mój Perl znajduje się w katalogu C:\Perl. Najpierw dwie małe modyfikacje źródeł:

Edytujemy plik C:\Qt\4.8.3\qt-everywhere-opensource-src-4.8.3\projects.pro:

} else:isEqual(PROJECT, examples) {
# SUBDIRS += examples
} else:isEqual(PROJECT, demos) {
# SUBDIRS += demos

Oczywiście, jeżeli masz zamiar zbudować od razu examplesy, to nie wykonujesz tego kroku. Następnie, edytujemy plik C:\Qt\4.8.3\qt-everywhere-opensource-src-4.8.3\mkspecs\win32-g++\qmake.conf:

QMAKE_CC = mingw32-gcc
QMAKE_CXX = mingw32-g++
QMAKE_LINK = mingw32-g++
QMAKE_LINK_C = mingw32-gcc
QMAKE_CFLAGS = -fno-keep-inline-dllexport
QMAKE_CFLAGS_RELEASE = -Os -fomit-frame-pointer -funroll-loops

Teraz przystępujemy do zorganizowania sobie środowiska budującego. W katalogu C:\Qt\4.8.3\mingw32 zakładamy pliki setvars.batprepare.bat oraz build.bat:

setvars.bat:

set PATH=C:\gcc\gcc-4.7.1-tdm\bin;C:\Perl\bin;
set INCLUDE=C:\gcc\gcc-4.7.1-tdm\include
set LIB=C:\gcc\gcc-4.7.1-tdm\lib
set LANG=en

prepare.bat:

call setvars.bat
C:\Qt\4.8.3\qt-everywhere-opensource-src-4.8.3\configure.exe ^
-opensource -confirm-license ^
-platform win32-g++ ^
-xplatform win32-g++ ^
-shared ^
-debug-and-release ^
-no-qt3support ^
-qt-style-windows ^
-qt-style-windowsxp ^
-qt-style-windowsvista ^
-no-phonon ^
-no-webkit ^
-plugin-sql-sqlite ^
-plugin-sql-odbc ^
-qt-zlib ^
-qt-libpng ^
-qt-libjpeg ^
-qt-libtiff ^
-qt-libmng ^
-no-openssl ^
-no-dsp ^
-no-vcproj ^
-no-incredibuild-xge ^
-no-cetest ^
-no-s60 ^
-fast ^
-I C:\gcc\gcc-4.7.1-tdm\include ^
-L C:\gcc\gcc-4.7.1-tdm\lib

build.bat:

call setvars.bat
mingw32-make -j 4

I konfigurujemy całe środowisko uruchamiając prepare.bat. Jeżeli będziemy chcieli coś zmienić w konfiguracji, trzeba usunąć wszystko, co powstało w tym katalogu i ponownie uruchomić prepare.bat. Przyjrzyj się dokładnie opcjom w pliku prepare.bat, jeżeli chcesz, możesz coś tam zmienić (np. możesz chcieć kompilować WebKit – ale ostrzegam, że to dość spora kobyła).

Jeżeli wszystko się udało, uruchamiamy build.bat i idziemy na długi spacer 🙂

 

PS.

Dla wersji 64-bitowej, postępujemy dokładnie tak samo. Zakładając, że  nasze gcc ma przedrostek x86_64-w64-mingw32, modyfikujemy tylko mkspecs i gotowe – można budować 🙂

 

PS2.
Okazuje się, że w Qt 4.8.3 jest jeden błąd, który należy naprawić, zanim uda nam się zbudować wersję 64-bitową z gcc. Poniższy fix jest brzydki, ale działa (uwaga: ten fix jest tylko i wyłącznie dla Windows, nie wolno go wprowadzać dla linuksa!). Edytujemy plik C:\Qt\4.8.3\qt-everywhere-opensource-src-4.8.3\src\3rdparty\javascriptcore\JavaScriptCore\jit\ExecutableAllocatorFixedVMPool.cpp:

zmieniamy:

#if ENABLE(EXECUTABLE_ALLOCATOR_FIXED) 

na:

#if 0 

Od tej pory można budować 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Wake on LAN vs. VPN

W naszym biurze weszło w życie zarządzenie (całkiem słusznie, zresztą…), aby niepotrzebne jednostki komputerowe na noc wyłączać. W zasadzie nie byłoby w ogóle problemu żadnego, ale od czasu do czasu zdarzają się sytuacje, gdy potrzebuję z domu wejść na swój biurowy komputer. Chcąc nie chcąc, musiałem przyjrzeć się tematowi Wake on LAN w ujęciu zdalnym.

Prace rozpocząłem oczywiście od przygotowania mojej jednostki do tego, aby mogła być włączana przez LAN. W przypadku Windows 7 są dwa miejsca, gdzie należy zajrzeć:  ustawienia karty sieciowej oraz usługa „Simple TCP/IP services„. Na poniższych screenach pokazuję, co i jak.

 

 

 

Gdy już wszystko przygotowane, sprawdzamy, czy rzeczywiście działa z poziomu sieci lokalnej. Na drugim komputerze windowsowym sprawdziłem dwa proste programiki: WakeMeOnLanoraz konsolowy Wake-on-LAN.

Oczywiście, do wysłania magic packeta, potrzebny jest adres MAC karty sieciowej w komputerze, który chcemy obudzić. Wszystko działało, jak należy, więc wyłączyłem komputer i poszedłem do domu 🙂

W domu podłączyłem się do firmowego VPN i próbuję. Niestety, okazało się, że nasz VPN nie przepuszcza (czy też: nie potrafi (lub nie chce) odtworzyć) magic packetów. Szkoda, bo to oznacza, że proces włączenia mojego komputera wymaga dodatkowego kroku: wejścia na jakiś już działający tam komputer i obudzenie mojego stamtąd, aby pakiet poszedł poprawdziwej sieci lokalnej, a nie po VPNie. Niby nic, ale jednak żal…

Na szczęście, wszystkie serwery chodzą w biurze 24/h, więc wszedłem na jeden z nich poprzez PuTTY (serwer działa pod kontrolą linuksa) i wydałem polecenie:

ether-wake 70:F3:XX:XX:XX:XX (tu adres MAC)

i odpaliłem pinga. Po chwili, ku mojej radości, ujrzałem odpowiedź z komputera, który pomyślnie się obudził 🙂  Chwała technologii Wake-on-LAN!

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Live Security Platinum

Przychodzi dzisiaj do mnie żona, pokazując na swoim laptopie okno, w którym ujrzałem dość długą i przerażającą listę wirusów, backdorów itp., wykrytych na Jej kompie. „Skąd Ona wzięła tego antywira?” przemknęło mi przez głowę i zacząłem przeglądać ową listę znalezionych robaczków. Szybko okazało się, że program nie daje żadnej możliwości wyleczenia i… kieruje na stronę oferującą wykup licencji. Dość drogiej, dodajmy… Zaświeciło światełko ostrzegawcze w głowie.

No nic – deinstalujemy te coś i bierzemy jakiś normalny skaner. Hmmm… nie daje się zdeinstalować? O… menadżer zadań zablokowany? O… cudowny program się właśnie wywalił, a jego proces nosił nazwę, złożoną z jego GUIDu?? Światło ostrzegawcze zmieniło się w alarm. Otóż „antywirus” o dźwięcznej nazwie Live Security Platinum (hehe) jest… sam w sobie wirusem (malware).

Jako pierwszy krok zaradczy, umożliwiający jakiekolwiek dalsze działania, wszedłem do trybu awaryjnego z obsługą sieci i uruchomiłem OTL. Wpisałem skrypt:

:OTL
O4 - HKLM..\Run: [KernelFaultCheck] %systemroot%\system32\dumprep 0 -k File not found
O4 - Startup: C:\Documents and Settings\All Users\Menu Start\Programy\Autostart\McAfee Security Scan Plus.lnk = C:\Program Files\McAfee Security Scan\3.0.207\SSScheduler.exe (McAfee, Inc.)
O20 - Winlogon\Notify\WgaLogon: DllName - (WgaLogon.dll) -  File not found

:Files
C:\Documents and Settings\All Users\Dane aplikacji\036E190802C09D33C8A50BA381CB3EF3
:Commands
[emptytemp]

 

i go uruchomiłem. Restart, znów OTL i jego funkcja czyszczenia. Restart.

Pobrałem Malwarebytes Anti-Malware. Szybki skan – wykrył resztki cudownego programu, a do tego dołożył jeszcze dwa inne skrypty. Raz-dwa je usunął, restart i – system czysty 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Windows: Czy podany adres jest adresem IP?

Potrzebowałem funkcji sprawdzającej, czy podany adres jest adresem z nazwą, czy adresem IP. Wiele nie myśląc napisałem:

bool Cu3Networking::isIPAddress(QString address)
{
    struct sockaddr_in sa;
    int result = inet_pton(AF_INET, address.toStdString().c_str(), &amp;(sa.sin_addr));
    return result != 0;
}

I wszystko fajnie, dopóki mnie user nie sprowadził na ziemię tekstem: „twój program nie uruchamia się na Windows XP!”. No tak… wciąż musimy zmagać się z systemem, który ma już ponad 10 lat na karku. Paranoja…

bool Cu3Networking::isIPAddress(QString address)
{
#ifdef WINXP_SUPPORT
    WSADATA wsaData;
    int result = WSAStartup(MAKEWORD(2, 2), &amp;wsaData);
    if (0 != result)
    {
        qDebug() << "WSAStartup failed: " << result;
        return false;
    }
    result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (0 != result)
    {
        qDebug() << "WSAStartup failed: " << result;
        return false;
    }
    unsigned long ulAddr = INADDR_NONE;
    ulAddr = inet_addr(address.toStdString().c_str());
    if (INADDR_NONE == ulAddr)
    {
        qDebug() << "inet_addr failed and returned INADDR_NONE";
        WSACleanup();
        return false;
    }
    if (INADDR_ANY == ulAddr)
    {
        qDebug() << "inet_addr failed and returned INADDR_ANY";
        WSACleanup();
        return false;
    }
    result = 1;
    WSACleanup();
#else
    struct sockaddr_in sa;
    int result = inet_pton(AF_INET, address.toStdString().c_str(), &(sa.sin_addr));
#endif
    return result != 0;
}

No i teraz działa. Pamiętać tylko należy o zdefiniowaniu flagi WINXP_SUPPORT.

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin