Wine-devel
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
January 2004
- 129 participants
- 360 discussions
Hello,
This message is largely for Chris Morgan's review, but I thought I
should open it to any interested parties.
I have added wave-in support for arts. I also fixed some bugs in the
waveout code, and made some "improvements". This code seems to work
really well for me. If no one sees any problems I will submit the
patch to the patches mailing list.
Here is a brief description of the changes I made:
(1) In ARTS_CloseDevice() (now named ARTS_CloseWaveOutDevice()) and
wodPlayer_NotifyWait()
I added 'wwo->sound_buffer = 0' after the HeapFree(). Without it,
HeapFree() was being called twice on the same buffer, and would crash
the second time.
(2) ARTS_AddRingMessage()
Added the same fix I submitted earlier for wineoss in regards to ring
buffer resizing.
(3) wodPlayer_WriteMaxFrags()
DWORD *bytes, points to the number of bytes the winearts driver thinks
it should be able to write. However, arts sometimes lies, and says it
has (for example) 512 bytes available, but when you call arts_write()
it returns zero bytes written.
In that case, I update *bytes to point to 0, otherwise
wodPlayer_WriteMaxFrags() will keep being called in vain.
(4a) wodPlayer_FeedDSP()
The old code had the following checks:
/* input queue empty and output buffer with no space */
if (!wwo->lpPlayPtr && availInQ) {
TRACE("Run out of wavehdr:s... flushing\n");
wwo->dwPlayedTotal = wwo->dwWrittenTotal;
return INFINITE;
}
if(!availInQ)
{
TRACE("no more room, no need to try to feed\n");
return wodPlayer_DSPWait(wwo);
}
I could not understand the purpose of the '&& availInQ' in the first
check. It seems that if we are out of wavehdrs, then it does not
matter if there is space in the availInQ or not, because we don't have
anything to write. So I removed the && availInQ part.
Looking at it now, I realize that maybe I need to add 'if (!availInQ)'
before 'wwo->dwPlayedTotal = wwo->dwWrittenTotal'?
(4b) wodPlayer_FeedDSP()
In the while loop, I could not figure out the purpose of 'availInQ >
SPACE_THRESHOLD', so I changed it to 'availInQ'. In reality, the
SPACE_THRESHOLD check could probably be left in, I am not sure what
the purpose was in the first place.
(4c) wodPlayer_FeedDSP()
the while loop in wodPlayer_FeedDSP() can terminate for two reasons:
+ availInQ == 0
+ ran out of waveHdrs to write.
In the first case, wodPlayer_FeedDSP() should return the number of
milliseconds before the buffer needs more data (which it currently
does). In the second case, it should return INFINTE, since it can't
feed anymore data until it gets some more wave headers. I added a bit
of code to handle the second case.
Notice also, that DSPWait() will only be called when the availInQ ==
0. This means that DSPWait() is always calculating the same value
which leads us to 4d.
(4d) Removed wodPlayer_DSPWait()
I just calculate the wait value once in wodOpen() and store it in
wwo->dwSleepTime.
(5) wodOpen ()
In wodOpen I changed the code from setting the ARTS_P_BUFFER_SIZE, to
setting the ARTS_P_PACKET_SETTINGS. This allows for finer control
which I needed in order to achieve lower latency.
The rest of the changes are for adding wave in support.
Jeremy Shaw.
--- orig/dlls/winmm/winearts/winearts.drv.spec
+++ mod/dlls/winmm/winearts/winearts.drv.spec
@@ -1,2 +1,3 @@
@ stdcall DriverProc(long long long long long) ARTS_DriverProc
@ stdcall wodMessage(long long long long long) ARTS_wodMessage
+@ stdcall widMessage(long long long long long) ARTS_widMessage
--- orig/dlls/winmm/winearts/audio.c
+++ mod/dlls/winmm/winearts/audio.c
@@ -32,8 +32,7 @@
* FIXME:
* pause in waveOut does not work correctly in loop mode
*
- * TODO:
- * implement wave-in support with artsc
+ * does something need to be done in for WaveIn DirectSound?
*/
/*#define EMULATE_SB16*/
@@ -65,10 +64,32 @@
#include <artsc.h>
-#define BUFFER_SIZE 16 * 1024
-#define SPACE_THRESHOLD 5 * 1024
+/* The following four #defines allow you to fine-tune the packet
+ * settings in arts for better low-latency support. You must also
+ * adjust the latency in the KDE arts control panel. I recommend 4
+ * fragments, 1024 bytes.
+ *
+ * The following is from the ARTS documentation and explains what CCCC
+ * and SSSS mean:
+ *
+ * @li ARTS_P_PACKET_SETTINGS (rw) This is a way to configure packet
+ * size & packet count at the same time. The format is 0xCCCCSSSS,
+ * where 2^SSSS is the packet size, and CCCC is the packet count. Note
+ * that when writing this, you don't necessarily get the settings you
+ * requested.
+ */
+#define WAVEOUT_PACKET_CCCC 0x000C
+#define WAVEOUT_PACKET_SSSS 0x0008
+#define WAVEIN_PACKET_CCCC 0x000C
+#define WAVEIN_PACKET_SSSS 0x0008
+
+#define BUFFER_REFILL_THRESHOLD 4
+
+#define WAVEOUT_PACKET_SETTINGS ((WAVEOUT_PACKET_CCCC << 16) | (WAVEOUT_PACKET_SSSS))
+#define WAVEIN_PACKET_SETTINGS ((WAVEIN_PACKET_CCCC << 16) | (WAVEIN_PACKET_SSSS))
#define MAX_WAVEOUTDRV (10)
+#define MAX_WAVEINDRV (10)
/* state diagram for waveOut writing:
*
@@ -96,7 +117,7 @@
/* events to be send to device */
enum win_wm_message {
WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
- WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
+ WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
};
typedef struct {
@@ -126,9 +147,12 @@
PCMWAVEFORMAT format;
WAVEOUTCAPSA caps;
+ DWORD dwSleepTime; /* Num of milliseconds to sleep between filling the dsp buffers */
+
/* arts information */
arts_stream_t play_stream; /* the stream structure we get from arts when opening a stream for playing */
DWORD dwBufferSize; /* size of whole buffer in bytes */
+ int packetSettings;
char* sound_buffer;
long buffer_size;
@@ -154,7 +178,29 @@
ARTS_MSG_RING msgRing;
} WINE_WAVEOUT;
+typedef struct {
+ volatile int state; /* one of the WINE_WS_ manifest constants */
+ WAVEOPENDESC waveDesc;
+ WORD wFlags;
+ PCMWAVEFORMAT format;
+ WAVEINCAPSA caps;
+
+ /* arts information */
+ arts_stream_t record_stream; /* the stream structure we get from arts when opening a stream for recording */
+ int packetSettings;
+
+ LPWAVEHDR lpQueuePtr;
+ DWORD dwRecordedTotal;
+
+ /* synchronization stuff */
+ HANDLE hStartUpEvent;
+ HANDLE hThread;
+ DWORD dwThreadID;
+ ARTS_MSG_RING msgRing;
+} WINE_WAVEIN;
+
static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
+static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
@@ -169,6 +215,8 @@
"WINE_WM_UPDATE",
"WINE_WM_BREAKLOOP",
"WINE_WM_CLOSING",
+ "WINE_WM_STARTING",
+ "WINE_WM_STOPPING",
};
/*======================================================================*
@@ -229,22 +277,35 @@
}
/******************************************************************
- * ARTS_CloseDevice
+ * ARTS_CloseWaveOutDevice
*
*/
-void ARTS_CloseDevice(WINE_WAVEOUT* wwo)
+void ARTS_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
{
arts_close_stream(wwo->play_stream); /* close the arts stream */
wwo->play_stream = (arts_stream_t*)-1;
/* free up the buffer we use for volume and reset the size */
if(wwo->sound_buffer)
+ {
HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
+ wwo->sound_buffer = 0;
+ }
wwo->buffer_size = 0;
}
/******************************************************************
+ * ARTS_CloseWaveInDevice
+ *
+ */
+void ARTS_CloseWaveInDevice(WINE_WAVEIN* wwi)
+{
+ arts_close_stream(wwi->record_stream); /* close the arts stream */
+ wwi->record_stream = (arts_stream_t*)-1;
+}
+
+/******************************************************************
* ARTS_Init
*/
static int ARTS_Init(void)
@@ -264,7 +325,15 @@
{
if(WOutDev[iDevice].play_stream != (arts_stream_t*)-1)
{
- ARTS_CloseDevice(&WOutDev[iDevice]);
+ ARTS_CloseWaveOutDevice(&WOutDev[iDevice]);
+ }
+ }
+
+ for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
+ {
+ if(WInDev[iDevice].record_stream != (arts_stream_t*)-1)
+ {
+ ARTS_CloseWaveInDevice(&WInDev[iDevice]);
}
}
@@ -331,7 +400,44 @@
WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
}
+ for (i = 0; i < MAX_WAVEINDRV; ++i)
+ {
+ WInDev[i].record_stream = (arts_stream_t*)-1;
+ memset(&WInDev[i].caps, 0, sizeof(WInDev[i].caps)); /* zero out
+ caps values */
+ /* FIXME: some programs compare this string against the content of the registry
+ * for MM drivers. The names have to match in order for the program to work
+ * (e.g. MS win9x mplayer.exe)
+ */
+#ifdef EMULATE_SB16
+ WInDev[i].caps.wMid = 0x0002;
+ WInDev[i].caps.wPid = 0x0104;
+ strcpy(WInDev[i].caps.szPname, "SB16 Wave In");
+#else
+ WInDev[i].caps.wMid = 0x00FF;
+ WInDev[i].caps.wPid = 0x0001;
+ strcpy(WInDev[i].caps.szPname,"CS4236/37/38");
+#endif
+ WInDev[i].caps.vDriverVersion = 0x0100;
+ WInDev[i].caps.dwFormats = 0x00000000;
+
+ WInDev[i].caps.wChannels = 2;
+
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
+ WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
+ WInDev[i].caps.wReserved1 = 0;
+ }
return 0;
}
@@ -376,9 +482,22 @@
EnterCriticalSection(&mr->msg_crst);
if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
{
+ int old_ring_buffer_size = mr->ring_buffer_size;
mr->ring_buffer_size += ARTS_RING_BUFFER_INCREMENT;
TRACE("mr->ring_buffer_size=%d\n",mr->ring_buffer_size);
mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
+ /* Now we need to rearrange the ring buffer so that the new
+ buffers just allocated are in between mr->msg_tosave and
+ mr->msg_toget.
+ */
+ if (mr->msg_tosave < mr->msg_toget)
+ {
+ memmove(&(mr->messages[mr->msg_toget + ARTS_RING_BUFFER_INCREMENT]),
+ &(mr->messages[mr->msg_toget]),
+ sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
+ );
+ mr->msg_toget += ARTS_RING_BUFFER_INCREMENT;
+ }
}
if (wait)
{
@@ -554,25 +673,6 @@
}
/**************************************************************************
- * wodPlayer_DSPWait [internal]
- * Returns the number of milliseconds to wait for the DSP buffer to clear.
- * This is based on the number of fragments we want to be clear before
- * writing and the number of free fragments we already have.
- */
-static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
-{
- int waitvalue = (wwo->dwBufferSize - arts_stream_get(wwo->play_stream,
- ARTS_P_BUFFER_SPACE)) / ((wwo->format.wf.nSamplesPerSec *
- wwo->format.wBitsPerSample * wwo->format.wf.nChannels)
- /1000);
-
- TRACE("wait value of %d\n", waitvalue);
-
- /* return the time left to play the buffer */
- return waitvalue;
-}
-
-/**************************************************************************
* wodPlayer_NotifyWait [internal]
* Returns the number of milliseconds to wait before attempting to notify
* completion of the specified wavehdr.
@@ -618,7 +718,10 @@
if(wwo->buffer_size < toWrite)
{
if(wwo->sound_buffer)
- HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
+ {
+ HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
+ wwo->sound_buffer = 0;
+ }
}
/* if we don't have a buffer then get one */
@@ -664,7 +767,11 @@
TRACE("written = %d\n", written);
- if (written <= 0) return written; /* if we wrote nothing just return */
+ if (written <= 0)
+ {
+ *bytes = 0; /* apparently arts is actually full */
+ return written; /* if we wrote nothing just return */
+ }
if (written >= dwLength)
wodPlayer_PlayPtrNext(wwo); /* If we wrote all current wavehdr, skip to the next one */
@@ -690,6 +797,23 @@
{
LPWAVEHDR lpWaveHdr;
+ if (wwo->lpQueuePtr) {
+ TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), reserved=(%ld), dwWrittenTotal=(%ld), force=(%d)\n",
+ wwo->lpQueuePtr,
+ wwo->lpPlayPtr,
+ wwo->lpLoopPtr,
+ wwo->lpQueuePtr->reserved,
+ wwo->dwWrittenTotal,
+ force);
+ } else {
+ TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), dwWrittenTotal=(%ld), force=(%d)\n",
+ wwo->lpQueuePtr,
+ wwo->lpPlayPtr,
+ wwo->lpLoopPtr,
+ wwo->dwWrittenTotal,
+ force);
+ }
+
/* Start from lpQueuePtr and keep notifying until:
* - we hit an unwritten wavehdr
* - we hit the beginning of a running loop
@@ -699,7 +823,7 @@
(force ||
(lpWaveHdr != wwo->lpPlayPtr &&
lpWaveHdr != wwo->lpLoopPtr &&
- lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
+ lpWaveHdr->reserved <= wwo->dwWrittenTotal))) {
wwo->lpQueuePtr = lpWaveHdr->lpNext;
@@ -854,8 +978,8 @@
availInQ = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE);
TRACE("availInQ = %ld\n", availInQ);
- /* input queue empty and output buffer with no space */
- if (!wwo->lpPlayPtr && availInQ) {
+ /* input queue empty */
+ if (!wwo->lpPlayPtr) {
TRACE("Run out of wavehdr:s... flushing\n");
wwo->dwPlayedTotal = wwo->dwWrittenTotal;
return INFINITE;
@@ -865,7 +989,7 @@
if(!availInQ)
{
TRACE("no more room, no need to try to feed\n");
- return wodPlayer_DSPWait(wwo);
+ return wwo->dwSleepTime;
}
/* Feed from partial wavehdr */
@@ -878,16 +1002,26 @@
/* Feed wavehdrs until we run out of wavehdrs or DSP space */
if (!wwo->dwPartialOffset)
{
- while(wwo->lpPlayPtr && availInQ > SPACE_THRESHOLD)
- {
- TRACE("feeding waveheaders until we run out of space\n");
- /* note the value that dwPlayedTotal will return when this wave finishes playing */
- wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
- wodPlayer_WriteMaxFrags(wwo, &availInQ);
- }
+ while(wwo->lpPlayPtr && availInQ)
+ {
+ TRACE("feeding waveheaders until we run out of space\n");
+ /* note the value that dwPlayedTotal will return when this wave finishes playing */
+ wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
+ TRACE("reserved=(%ld) dwWrittenTotal=(%ld) dwBufferLength=(%ld)\n",
+ wwo->lpPlayPtr->reserved,
+ wwo->dwWrittenTotal,
+ wwo->lpPlayPtr->dwBufferLength
+ );
+ wodPlayer_WriteMaxFrags(wwo, &availInQ);
+ }
}
- return wodPlayer_DSPWait(wwo);
+ if (!wwo->lpPlayPtr) {
+ TRACE("Ran out of wavehdrs\n");
+ return INFINITE;
+ }
+
+ return wwo->dwSleepTime;
}
@@ -1010,14 +1144,19 @@
if(!wwo->play_stream) return MMSYSERR_ALLOCATED;
- /* Try to set buffer size from constant and store the value that it
- was set to for future use */
- wwo->dwBufferSize = arts_stream_set(wwo->play_stream,
- ARTS_P_BUFFER_SIZE, BUFFER_SIZE);
- TRACE("Tried to set BUFFER_SIZE of %d, wwo->dwBufferSize is actually %ld\n", BUFFER_SIZE, wwo->dwBufferSize);
+ /* Try to set the packet settings from constant and store the value that it
+ was actually set to for future use */
+ wwo->packetSettings = arts_stream_set(wwo->play_stream, ARTS_P_PACKET_SETTINGS, WAVEOUT_PACKET_SETTINGS);
+ TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEOUT_PACKET_SETTINGS, wwo->packetSettings);
+
+ wwo->dwBufferSize = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SIZE);
+ TRACE("Buffer size is now (%ld)\n",wwo->dwBufferSize);
+
wwo->dwPlayedTotal = 0;
wwo->dwWrittenTotal = 0;
+ wwo->dwSleepTime = ((1 << (wwo->packetSettings & 0xFFFF)) * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->format.wf.nAvgBytesPerSec;
+
/* Initialize volume to full level */
wwo->volume_left = 100;
wwo->volume_right = 100;
@@ -1076,7 +1215,7 @@
ARTS_DestroyRingMessage(&wwo->msgRing);
- ARTS_CloseDevice(wwo); /* close the stream and clean things up */
+ ARTS_CloseWaveOutDevice(wwo); /* close the stream and clean things up */
ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
}
@@ -1399,6 +1538,499 @@
}
/*======================================================================*
+ * Low level WAVE IN implementation *
+ *======================================================================*/
+
+/**************************************************************************
+ * widGetNumDevs [internal]
+ */
+static DWORD widGetNumDevs(void)
+{
+ TRACE("%d \n",MAX_WAVEINDRV);
+ return MAX_WAVEINDRV;
+}
+
+/**************************************************************************
+ * widNotifyClient [internal]
+ */
+static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case WIM_OPEN:
+ case WIM_CLOSE:
+ case WIM_DATA:
+ if (wwi->wFlags != DCB_NULL &&
+ !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
+ (HDRVR)wwi->waveDesc.hWave, wMsg,
+ wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
+ WARN("can't notify client !\n");
+ return MMSYSERR_ERROR;
+ }
+ break;
+ default:
+ FIXME("Unknown callback message %u\n", wMsg);
+ return MMSYSERR_INVALPARAM;
+ }
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widGetDevCaps [internal]
+ */
+static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
+{
+ TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
+
+ if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+
+ if (wDevID >= MAX_WAVEINDRV) {
+ TRACE("MAX_WAVINDRV reached !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widRecorder [internal]
+ */
+static DWORD CALLBACK widRecorder(LPVOID pmt)
+{
+ WORD uDevID = (DWORD)pmt;
+ WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
+ WAVEHDR* lpWaveHdr;
+ DWORD dwSleepTime;
+ DWORD bytesRead;
+ int dwBufferSpace;
+ enum win_wm_message msg;
+ DWORD param;
+ HANDLE ev;
+
+ SetEvent(wwi->hStartUpEvent);
+
+ /* make sleep time to be # of ms to record one packet */
+ dwSleepTime = ((1 << (wwi->packetSettings & 0xFFFF)) * 1000) / wwi->format.wf.nAvgBytesPerSec;
+ TRACE("sleeptime=%ld ms\n", dwSleepTime);
+
+ for(;;) {
+ /* Oddly enough, dwBufferSpace is sometimes negative....
+ *
+ * NOTE: If you remove this call to arts_stream_get() and
+ * remove the && (dwBufferSpace > 0) the code will still
+ * function correctly. I don't know which way is
+ * faster/better.
+ */
+ dwBufferSpace = arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SPACE);
+ TRACE("wwi->lpQueuePtr=(%p), wwi->state=(%d), dwBufferSpace=(%d)\n",wwi->lpQueuePtr,wwi->state,dwBufferSpace);
+
+ /* read all data is arts input buffer. */
+ if ((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING) && (dwBufferSpace > 0))
+ {
+ lpWaveHdr = wwi->lpQueuePtr;
+
+ TRACE("read as much as we can\n");
+ while(wwi->lpQueuePtr)
+ {
+ TRACE("attempt to read %ld bytes\n",lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
+ bytesRead = arts_read(wwi->record_stream,
+ lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
+ lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
+ TRACE("bytesRead=%ld\n",bytesRead);
+ if (bytesRead==0) break;
+
+ lpWaveHdr->dwBytesRecorded += bytesRead;
+ wwi->dwRecordedTotal += bytesRead;
+
+ /* buffer full. notify client */
+ if (lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength)
+ {
+ /* must copy the value of next waveHdr, because we have no idea of what
+ * will be done with the content of lpWaveHdr in callback
+ */
+ LPWAVEHDR lpNext = lpWaveHdr->lpNext;
+
+ TRACE("waveHdr full.\n");
+
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+ lpWaveHdr = wwi->lpQueuePtr = lpNext;
+ }
+ }
+ }
+
+ /* wait for dwSleepTime or an event in thread's queue */
+ WaitForSingleObject(wwi->msgRing.msg_event, dwSleepTime);
+
+ while (ARTS_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
+ {
+ TRACE("msg=%s param=0x%lx\n",wodPlayerCmdString[msg - WM_USER - 1], param);
+ switch(msg) {
+ case WINE_WM_PAUSING:
+ wwi->state = WINE_WS_PAUSED;
+
+ /* Put code here to "pause" arts recording
+ */
+
+ SetEvent(ev);
+ break;
+ case WINE_WM_STARTING:
+ wwi->state = WINE_WS_PLAYING;
+
+ /* Put code here to "start" arts recording
+ */
+
+ SetEvent(ev);
+ break;
+ case WINE_WM_HEADER:
+ lpWaveHdr = (LPWAVEHDR)param;
+ /* insert buffer at end of queue */
+ {
+ LPWAVEHDR* wh;
+ int num_headers = 0;
+ for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
+ {
+ num_headers++;
+
+ }
+ *wh=lpWaveHdr;
+ }
+ break;
+ case WINE_WM_STOPPING:
+ if (wwi->state != WINE_WS_STOPPED)
+ {
+
+ /* Put code here to "stop" arts recording
+ */
+
+ /* return current buffer to app */
+ lpWaveHdr = wwi->lpQueuePtr;
+ if (lpWaveHdr)
+ {
+ LPWAVEHDR lpNext = lpWaveHdr->lpNext;
+ TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+ wwi->lpQueuePtr = lpNext;
+ }
+ }
+ wwi->state = WINE_WS_STOPPED;
+ SetEvent(ev);
+ break;
+ case WINE_WM_RESETTING:
+ wwi->state = WINE_WS_STOPPED;
+ wwi->dwRecordedTotal = 0;
+
+ /* return all buffers to the app */
+ for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
+ TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+ }
+ wwi->lpQueuePtr = NULL;
+ SetEvent(ev);
+ break;
+ case WINE_WM_CLOSING:
+ wwi->hThread = 0;
+ wwi->state = WINE_WS_CLOSED;
+ SetEvent(ev);
+ ExitThread(0);
+ /* shouldn't go here */
+ default:
+ FIXME("unknown message %d\n", msg);
+ break;
+ }
+ }
+ }
+ ExitThread(0);
+ /* just for not generating compilation warnings... should never be executed */
+ return 0;
+}
+
+/**************************************************************************
+ * widOpen [internal]
+ */
+static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
+{
+ WINE_WAVEIN* wwi;
+
+ TRACE("(%u, %p %08lX);\n",wDevID, lpDesc, dwFlags);
+ if (lpDesc == NULL) {
+ WARN("Invalid Parametr (lpDesc == NULL)!\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (wDevID >= MAX_WAVEINDRV) {
+ TRACE ("MAX_WAVEINDRV reached !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ /* if this device is already open tell the app that it is allocated */
+ if(WInDev[wDevID].record_stream != (arts_stream_t*)-1)
+ {
+ TRACE("device already allocated\n");
+ return MMSYSERR_ALLOCATED;
+ }
+
+ /* only PCM format is support so far... */
+ if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
+ lpDesc->lpFormat->nChannels == 0 ||
+ lpDesc->lpFormat->nSamplesPerSec == 0) {
+ WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+ lpDesc->lpFormat->nSamplesPerSec);
+ return WAVERR_BADFORMAT;
+ }
+
+ if (dwFlags & WAVE_FORMAT_QUERY) {
+ TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+ lpDesc->lpFormat->nSamplesPerSec);
+ return MMSYSERR_NOERROR;
+ }
+
+ wwi = &WInDev[wDevID];
+
+ /* direct sound not supported, ignore the flag */
+ dwFlags &= ~WAVE_DIRECTSOUND;
+
+ wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+
+ memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
+ memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
+
+ if (wwi->format.wBitsPerSample == 0) {
+ WARN("Resetting zerod wBitsPerSample\n");
+ wwi->format.wBitsPerSample = 8 *
+ (wwi->format.wf.nAvgBytesPerSec /
+ wwi->format.wf.nSamplesPerSec) /
+ wwi->format.wf.nChannels;
+ }
+
+ wwi->record_stream = arts_record_stream(wwi->format.wf.nSamplesPerSec,
+ wwi->format.wBitsPerSample,
+ wwi->format.wf.nChannels,
+ "winearts");
+ TRACE("(wwi->record_stream=%p)\n",wwi->record_stream);
+ wwi->state = WINE_WS_STOPPED;
+
+ wwi->packetSettings = arts_stream_set(wwi->record_stream, ARTS_P_PACKET_SETTINGS, WAVEIN_PACKET_SETTINGS);
+ TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEIN_PACKET_SETTINGS, wwi->packetSettings);
+ TRACE("Buffer size is now (%d)\n", arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SIZE));
+
+ if (wwi->lpQueuePtr) {
+ WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
+ wwi->lpQueuePtr = NULL;
+ }
+ arts_stream_set(wwi->record_stream, ARTS_P_BLOCKING, 0); /* disable blocking on this stream */
+
+ if(!wwi->record_stream) return MMSYSERR_ALLOCATED;
+
+ wwi->dwRecordedTotal = 0;
+ wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+
+ ARTS_InitRingMessage(&wwi->msgRing);
+
+ /* create recorder thread */
+ if (!(dwFlags & WAVE_DIRECTSOUND)) {
+ wwi->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+ wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
+ WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
+ CloseHandle(wwi->hStartUpEvent);
+ } else {
+ wwi->hThread = INVALID_HANDLE_VALUE;
+ wwi->dwThreadID = 0;
+ }
+ wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
+
+ TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
+ wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
+ wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
+ wwi->format.wf.nBlockAlign);
+ return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
+}
+
+/**************************************************************************
+ * widClose [internal]
+ */
+static DWORD widClose(WORD wDevID)
+{
+ WINE_WAVEIN* wwi;
+
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't close !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ wwi = &WInDev[wDevID];
+
+ if (wwi->lpQueuePtr != NULL) {
+ WARN("still buffers open !\n");
+ return WAVERR_STILLPLAYING;
+ }
+
+ ARTS_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
+ ARTS_CloseWaveInDevice(wwi);
+ wwi->state = WINE_WS_CLOSED;
+ ARTS_DestroyRingMessage(&wwi->msgRing);
+ return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
+}
+
+/**************************************************************************
+ * widAddBuffer [internal]
+ */
+static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+
+ if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't do it !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+ if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
+ TRACE("never been prepared !\n");
+ return WAVERR_UNPREPARED;
+ }
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
+ TRACE("header already in use !\n");
+ return WAVERR_STILLPLAYING;
+ }
+
+ lpWaveHdr->dwFlags |= WHDR_INQUEUE;
+ lpWaveHdr->dwFlags &= ~WHDR_DONE;
+ lpWaveHdr->dwBytesRecorded = 0;
+ lpWaveHdr->lpNext = NULL;
+
+ ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widPrepare [internal]
+ */
+static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+
+ if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
+
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveHdr->dwFlags |= WHDR_PREPARED;
+ lpWaveHdr->dwFlags &= ~WHDR_DONE;
+ lpWaveHdr->dwBytesRecorded = 0;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widUnprepare [internal]
+ */
+static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+ if (wDevID >= MAX_WAVEINDRV) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
+ TRACE("Still playing...\n");
+ return WAVERR_STILLPLAYING;
+ }
+
+ lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widStart [internal]
+ */
+static DWORD widStart(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't start recording !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widStop [internal]
+ */
+static DWORD widStop(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't stop !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widReset [internal]
+ */
+static DWORD widReset(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't reset !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+ ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widMessage (WINEARTS.6)
+ */
+DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
+ wDevID, wMsg, dwUser, dwParam1, dwParam2);
+ switch (wMsg) {
+ case DRVM_INIT:
+ case DRVM_EXIT:
+ case DRVM_ENABLE:
+ case DRVM_DISABLE:
+ /* FIXME: Pretend this is supported */
+ return 0;
+ case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
+ case WIDM_CLOSE: return widClose (wDevID);
+ case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
+ case WIDM_GETNUMDEVS: return widGetNumDevs ();
+ case WIDM_RESET: return widReset (wDevID);
+ case WIDM_START: return widStart (wDevID);
+ case WIDM_STOP: return widStop (wDevID);
+ default:
+ FIXME("unknown message %d!\n", wMsg);
+ }
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+/*======================================================================*
* Low level DSOUND implementation *
*======================================================================*/
static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
@@ -1430,6 +2062,16 @@
* wodMessage (WINEARTS.@)
*/
DWORD WINAPI ARTS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
+ return MMSYSERR_NOTENABLED;
+}
+
+/**************************************************************************
+ * widMessage (WINEARTS.6)
+ */
+DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
DWORD dwParam1, DWORD dwParam2)
{
FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3
4
> p.s. If someone out there is thinking: of course you could do that,
> if would be trivial if you just did 'X', please feel free to share X
> with me. Otherwise, we'll have to investigate the open pbx project
> and the possibility of getting some Quicknet cards...
I have an 8 port conference bridge we can use. Unfortunately
I can't provide toll free access. It will be long distance from
MN too because I can't bring the PBX.
If there's going to be more than 8 callers, I have another bridge
available that will accomodate 32. I prefer not to use that one
though.
The neat solution would be to use the asterisk.org software to set
up a PBX with a SIP stack. Then international folks could just use
the Internet to participate. I've never used Asterisk though, so
it's hard to say how much work would be involved setting that up.
Is there a local LUG that would be willing to set something like
that up? I imagine there's some geek in MN with all the equipment
in his closet.
-brian
1
0
Hi,
I don't know who maintains the mandrake packages, but it appears that they
incorrectly install libwine.so.1 into /usr/lib/wine instead of /usr/lib,
and therefore all Wine apps fail to start with an unresolved import
failure.
Could whoever is responsible for these packages please double check the
specfiles?
thanks -mike
1
0
Hello,
I had the same problem as Oleg Prokhorov (no keyboard input in
fullscreen games) and following patch fixed it
(just a single line in Randr and XVid drivers). I do not know if
something gets broken by it; I have tested it with Railroad Tycoon II
demo.
--- wine-20031212/dlls/x11drv/xvidmode.c.orig 2003-11-26
19:59:36.000000000 -0500
+++ wine-20031212/dlls/x11drv/xvidmode.c 2004-01-06
15:57:58.000000000 -0500
@@ -143,6 +143,7 @@
#else
XWarpPointer(gdi_display, None, DefaultRootWindow(gdi_display), 0, 0,
0, 0, 0, 0);
#endif
+ XSetInputFocus(gdi_display, DefaultRootWindow(gdi_display),
RevertToPointerRoot, CurrentTime);
XSync(gdi_display, False);
wine_tsx11_unlock();
}
--- wine-20031212/dlls/x11drv/xrandr.c.orig 2004-01-06
16:00:30.000000000 -0500
+++ wine-20031212/dlls/x11drv/xrandr.c 2004-01-06 16:00:10.000000000
-0500
@@ -174,6 +174,8 @@
{
ERR("Resolution change not successful -- perhaps display has
chaned?");
}
+ XSetInputFocus(gdi_display, DefaultRootWindow(gdi_display),
RevertToPointerRoot, CurrentTime);
+
XRRFreeScreenConfigInfo(sc);
wine_tsx11_unlock();
}
Thank you for great work, wine developers!
--
::Robert Glowczynski
::email: robertg(a)aurox.org
::Aurox Core Team http://www.aurox.org
::http://www.distrowatch.com/table.php?distribution=aurox
2
1
Hello,
is the www.winehq.org website down? The host is pingable and port 80 is
open but i get no data after the initial tcp connect.
bye
michael
--
Michael Stefaniuc Tel.: +49-711-96437-199
System Administration Fax.: +49-711-96437-111
Red Hat GmbH Email: mstefani(a)redhat.com
Hauptstaetterstr. 58 http://www.redhat.de/
D-70178 Stuttgart
3
2
Sorry all, This is way way way _way_ overdue. But it is really the first
time i have had a chance to really take some time and review the changes
Alexandre gave me, retest everything and put together a patch against
the tip.
I did some quick testing and it appears that this will work.
I have broken it into 2 patches
wine_wintab.diff is all the wintab work except for the x11drv stuff
wine_x11_wintab.diff is the changes to x11drv. the reason for this is
because i still have the ime patch that i submitted earlier in my wine
tree so i had to had edit that x11 patch to try to make it not dependent
on the xim stuff being already applied. This make there a chance that
there are bits that may not apply cleanly, and if you are committing
both of my patches (xim and this) then they wont apply cleanly without a
bit of fudging.
I am also giving you 2 test applications i used for wintab testing.
SYSPRESS.EXE and TILTTEST.EXE These applications helped alot on testing
things. They where found on the wacom developers site, i think i still
have the source kicking around somewhere.
Changelog: Enables Tablet support with both Tilt and Pressure
-aric
Index: dlls/wintab32/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/wintab32/Makefile.in,v
retrieving revision 1.3
diff -u -w -r1.3 Makefile.in
--- dlls/wintab32/Makefile.in 11 Oct 2003 01:09:16 -0000 1.3
+++ dlls/wintab32/Makefile.in 8 Jan 2004 14:57:45 -0000
@@ -3,14 +3,15 @@
SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = wintab32.dll
-IMPORTS = kernel32
+IMPORTS = kernel32 user32
ALTNAMES = wintab.dll
SPEC_SRCS16 = $(ALTNAMES:.dll=.spec)
C_SRCS = \
context.c \
- manager.c
+ manager.c \
+ wintab32.c
C_SRCS16 = \
wintab16.c
Index: dlls/wintab32/context.c
===================================================================
RCS file: /home/wine/wine/dlls/wintab32/context.c,v
retrieving revision 1.2
diff -u -w -r1.2 context.c
--- dlls/wintab32/context.c 5 Sep 2003 23:08:26 -0000 1.2
+++ dlls/wintab32/context.c 8 Jan 2004 14:57:46 -0000
@@ -2,6 +2,7 @@
* Tablet Context
*
* Copyright 2002 Patrik Stridvall
+ * Copyright 2003 CodeWeavers, Aric Stewart
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,29 +20,340 @@
*/
#include "config.h"
-
+#include <stdio.h>
+#include <stdlib.h>
#include <stdarg.h>
#include "windef.h"
-#include "winbase.h"
#include "winerror.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "user.h"
#include "wintab.h"
+#include "wintab_internal.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(wintab32);
+/*
+ * Documentation found at
+ * http://www.csl.sony.co.jp/projects/ar/restricted/wintabl.html
+ */
+
+BOOL gLoaded= FALSE;
+
+LPOPENCONTEXT gOpenContexts = NULL;
+INT gContextCount = 0;
+HCTX gTopContext = (HCTX)0xc00;
+
+extern HWND hwndDefault;
+extern CRITICAL_SECTION csTablet;
+
+char* DUMPBITS(int x, char* buf)
+{
+ strcpy(buf,"{");
+ if (x&PK_CONTEXT) strcat(buf,"PK_CONTEXT ");
+ if (x&PK_STATUS) strcat(buf, "PK_STATUS ");
+ if (x&PK_TIME) strcat(buf, "PK_TIME ");
+ if (x&PK_CHANGED) strcat(buf, "PK_CHANGED ");
+ if (x&PK_SERIAL_NUMBER) strcat(buf, "PK_SERIAL_NUMBER ");
+ if (x&PK_CURSOR) strcat(buf, "PK_CURSOR ");
+ if (x&PK_BUTTONS) strcat(buf, "PK_BUTTONS ");
+ if (x&PK_X) strcat(buf, "PK_X ");
+ if (x&PK_Y) strcat(buf, "PK_Y ");
+ if (x&PK_Z) strcat(buf, "PK_Z ");
+ if (x&PK_NORMAL_PRESSURE) strcat(buf, "PK_NORMAL_PRESSURE ");
+ if (x&PK_TANGENT_PRESSURE) strcat(buf, "PK_TANGENT_PRESSURE ");
+ if (x&PK_ORIENTATION) strcat(buf, "PK_ORIENTATION ");
+ if (x&PK_ROTATION) strcat(buf, "PK_ROTATION ");
+ strcat(buf, "}");
+ return buf;
+}
+
+static inline void DUMPPACKET(WTPACKET packet)
+{
+ TRACE("pkContext: 0x%x pkStatus: 0x%x pkTime : 0x%x pkChanged: 0x%x pkSerialNumber: 0x%x pkCursor : %i pkButtons: %x pkX: %li pkY: %li pkZ: %li pkNormalPressure: %i pkTangentPressure: %i pkOrientation: (%i,%i,%i) pkRotation: (%i,%i,%i)\n"
+,(UINT)packet.pkContext,
+ (UINT)packet.pkStatus,
+ (UINT)packet.pkTime,
+ (UINT)packet.pkChanged,
+ packet.pkSerialNumber,
+ packet.pkCursor,
+ (UINT)packet.pkButtons,
+ packet.pkX,
+ packet.pkY,
+ packet.pkZ,
+ packet.pkNormalPressure,
+ packet.pkTangentPressure,
+ packet.pkOrientation.orAzimuth,
+ packet.pkOrientation.orAltitude, packet.pkOrientation.orTwist,
+ packet.pkRotation.roPitch,
+ packet.pkRotation.roRoll, packet.pkRotation.roYaw);
+}
+
+static inline void DUMPCONTEXT(LOGCONTEXTA lc)
+{
+ CHAR mmsg[4000];
+ CHAR bits[100];
+ CHAR bits1[100];
+ CHAR bits2[100];
+
+ sprintf(mmsg,"%s, %x, %x, %x, %x, %x, %x, %x%s, %x%s, %x%s, %x, %x, %i, %i, %i, %li ,%li, %li, %li, %li, %li,%li, %li, %li, %li, %li, %li, %i, %i, %i, %i, %i %li %li\n",
+ debugstr_a(lc.lcName), lc.lcOptions, lc.lcStatus, lc.lcLocks, lc.lcMsgBase,
+lc.lcDevice, lc.lcPktRate, (UINT)lc.lcPktData, DUMPBITS(lc.lcPktData,bits),
+(UINT)lc.lcPktMode, DUMPBITS(lc.lcPktMode,bits1), (UINT)lc.lcMoveMask,
+DUMPBITS(lc.lcMoveMask,bits2), (INT)lc.lcBtnDnMask, (INT)lc.lcBtnUpMask,
+(INT)lc.lcInOrgX, (INT)lc.lcInOrgY, (INT)lc.lcInOrgZ, lc.lcInExtX, lc.lcInExtY,
+lc.lcInExtZ, lc.lcOutOrgX, lc.lcOutOrgY, lc.lcOutOrgZ, lc.lcOutExtX,
+lc.lcOutExtY, lc.lcOutExtZ, lc.lcSensX, lc.lcSensY, lc.lcSensZ, lc.lcSysMode,
+lc.lcSysOrgX, lc.lcSysOrgY, lc.lcSysExtX, lc.lcSysExtY, lc.lcSysSensX,
+lc.lcSysSensY);
+ TRACE("context: %s",mmsg);
+}
+
+
+/* Find an open context given the handle */
+LPOPENCONTEXT TABLET_FindOpenContext(HCTX hCtx)
+{
+ LPOPENCONTEXT ptr = gOpenContexts;
+ while (ptr)
+ {
+ if (ptr->handle == hCtx) return ptr;
+ ptr = ptr->next;
+ }
+ return NULL;
+}
+
+static void LoadTablet()
+{
+ TRACE("Initilizing the tablet to hwnd 0x%x\n",(INT)hwndDefault);
+ gLoaded= TRUE;
+ pLoadTabletInfo(hwndDefault);
+}
+
+int TABLET_PostTabletMessage(LPOPENCONTEXT newcontext, UINT msg, WPARAM wParam,
+ LPARAM lParam, BOOL send_always)
+{
+ if ((send_always) || (newcontext->context.lcOptions & CXO_MESSAGES))
+ {
+ TRACE("Posting message %x to %x\n",msg, (UINT)newcontext->hwndOwner);
+ return PostMessageA(newcontext->hwndOwner, msg, wParam, lParam);
+ }
+ return 0;
+}
+
+static inline DWORD ScaleForContext(DWORD In, DWORD InOrg, DWORD InExt, DWORD
+ OutOrg, DWORD OutExt)
+{
+ if (((InExt > 0 )&&(OutExt > 0)) || ((InExt<0) && (OutExt < 0)))
+ return ((In - InOrg) * abs(OutExt) / abs(InExt)) + OutOrg;
+ else
+ return ((abs(InExt) - (In - InOrg))*abs(OutExt) / abs(InExt)) + OutOrg;
+}
+
+LPOPENCONTEXT FindOpenContext(HWND hwnd)
+{
+ LPOPENCONTEXT ptr=NULL;
+ ptr = gOpenContexts;
+ while (ptr)
+ {
+ TRACE("Trying Context %lx (%x %x)\n",(LONG)ptr->handle,(INT)hwnd,
+ (INT)ptr->hwndOwner);
+
+ if (ptr->hwndOwner == hwnd)
+ return ptr;
+ }
+ return NULL;
+}
+
+LPOPENCONTEXT AddPacketToContextQueue(LPWTPACKET packet, HWND hwnd)
+{
+ LPOPENCONTEXT ptr=NULL;
+
+ EnterCriticalSection(&csTablet);
+
+ ptr = gOpenContexts;
+ while (ptr)
+ {
+ TRACE("Trying Queue %lx (%x %x)\n",(LONG)ptr->handle,(INT)hwnd,
+ (INT)ptr->hwndOwner);
+
+ if (ptr->hwndOwner == hwnd)
+ {
+ int tgt;
+ if (!ptr->enabled)
+ {
+ ptr = ptr->next;
+ continue;
+ }
+
+ tgt = ptr->PacketsQueued;
+
+ packet->pkContext = ptr->handle;
+
+ /* translate packet data to the context */
+
+ /* Scale as per documentation */
+ packet->pkY = ScaleForContext(packet->pkY, ptr->context.lcInOrgY,
+ ptr->context.lcInExtY, ptr->context.lcOutOrgY,
+ ptr->context.lcOutExtY);
+
+ packet->pkX = ScaleForContext(packet->pkX, ptr->context.lcInOrgX,
+ ptr->context.lcInExtX, ptr->context.lcOutOrgX,
+ ptr->context.lcOutExtX);
+
+ /* flip the Y axis */
+ if (ptr->context.lcOutExtY > 0)
+ packet->pkY = ptr->context.lcOutExtY - packet->pkY;
+
+ DUMPPACKET(*packet);
+
+ if (tgt + 1 == ptr->QueueSize)
+ {
+ TRACE("Queue Overflow %p\n",ptr->handle);
+ packet->pkStatus = TPS_QUEUE_ERR;
+ }
+ else
+ {
+ TRACE("Placed in queue %p index %i\n",ptr->handle,tgt);
+ memcpy(&ptr->PacketQueue[tgt], packet, sizeof
+ (WTPACKET));
+ ptr->PacketsQueued++;
+
+ if (ptr->ActiveCursor != packet->pkCursor)
+ {
+ ptr->ActiveCursor = packet->pkCursor;
+ if (ptr->context.lcOptions & CXO_CSRMESSAGES)
+ TABLET_PostTabletMessage(ptr, WT_CSRCHANGE,
+ (WPARAM)packet->pkSerialNumber, (LPARAM)ptr->handle,
+ FALSE);
+ }
+ }
+ break;
+ }
+ ptr = ptr->next;
+ }
+ LeaveCriticalSection(&csTablet);
+ TRACE("Done (%p)\n",ptr);
+ return ptr;
+}
+
+int static inline CopyTabletData(LPVOID target, LPVOID src, INT size)
+{
+ memcpy(target,src,size);
+ return(size);
+}
+
+static INT TABLET_FindPacket(LPOPENCONTEXT context, UINT wSerial,
+ LPWTPACKET *pkt)
+{
+ int loop;
+ int index = -1;
+ for (loop = 0; loop < context->PacketsQueued; loop++)
+ if (context->PacketQueue[loop].pkSerialNumber == wSerial)
+ {
+ index = loop;
+ *pkt = &context->PacketQueue[loop];
+ break;
+ }
+
+ TRACE("%i .. %i\n",context->PacketsQueued,index);
+
+ return index;
+}
+
+
+static LPVOID TABLET_CopyPacketData(LPOPENCONTEXT context, LPVOID lpPkt,
+ LPWTPACKET wtp)
+{
+ LPBYTE ptr;
+ CHAR bits[100];
+
+ ptr = lpPkt;
+ TRACE("Packet Bits %s\n",DUMPBITS(context->context.lcPktData,bits));
+
+ if (context->context.lcPktData & PK_CONTEXT)
+ ptr+=CopyTabletData(ptr,&wtp->pkContext,sizeof(HCTX));
+ if (context->context.lcPktData & PK_STATUS)
+ ptr+=CopyTabletData(ptr,&wtp->pkStatus,sizeof(UINT));
+ if (context->context.lcPktData & PK_TIME)
+ ptr+=CopyTabletData(ptr,&wtp->pkTime,sizeof(LONG));
+ if (context->context.lcPktData & PK_CHANGED)
+ ptr+=CopyTabletData(ptr,&wtp->pkChanged,sizeof(WTPKT));
+ if (context->context.lcPktData & PK_SERIAL_NUMBER)
+ ptr+=CopyTabletData(ptr,&wtp->pkChanged,sizeof(UINT));
+ if (context->context.lcPktData & PK_CURSOR)
+ ptr+=CopyTabletData(ptr,&wtp->pkCursor,sizeof(UINT));
+ if (context->context.lcPktData & PK_BUTTONS)
+ ptr+=CopyTabletData(ptr,&wtp->pkButtons,sizeof(DWORD));
+ if (context->context.lcPktData & PK_X)
+ ptr+=CopyTabletData(ptr,&wtp->pkX,sizeof(DWORD));
+ if (context->context.lcPktData & PK_Y)
+ ptr+=CopyTabletData(ptr,&wtp->pkY,sizeof(DWORD));
+ if (context->context.lcPktData & PK_Z)
+ ptr+=CopyTabletData(ptr,&wtp->pkZ,sizeof(DWORD));
+ if (context->context.lcPktData & PK_NORMAL_PRESSURE)
+ ptr+=CopyTabletData(ptr,&wtp->pkNormalPressure,sizeof(UINT));
+ if (context->context.lcPktData & PK_TANGENT_PRESSURE)
+ ptr+=CopyTabletData(ptr,&wtp->pkTangentPressure,sizeof(UINT));
+ if (context->context.lcPktData & PK_ORIENTATION)
+ ptr+=CopyTabletData(ptr,&wtp->pkOrientation,sizeof(ORIENTATION));
+ if (context->context.lcPktData & PK_ROTATION)
+ ptr+=CopyTabletData(ptr,&wtp->pkRotation,sizeof(ROTATION));
+
+ //TRACE("Copied %i bytes\n",(INT)ptr - (INT)lpPkt);
+ return ptr;
+}
+
+static VOID TABLET_BlankPacketData(LPOPENCONTEXT context, LPVOID lpPkt, INT n)
+{
+ int rc = 0;
+
+ if (context->context.lcPktData & PK_CONTEXT)
+ rc +=sizeof(HCTX);
+ if (context->context.lcPktData & PK_STATUS)
+ rc +=sizeof(UINT);
+ if (context->context.lcPktData & PK_TIME)
+ rc += sizeof(LONG);
+ if (context->context.lcPktData & PK_CHANGED)
+ rc += sizeof(WTPKT);
+ if (context->context.lcPktData & PK_SERIAL_NUMBER)
+ rc += sizeof(UINT);
+ if (context->context.lcPktData & PK_CURSOR)
+ rc += sizeof(UINT);
+ if (context->context.lcPktData & PK_BUTTONS)
+ rc += sizeof(DWORD);
+ if (context->context.lcPktData & PK_X)
+ rc += sizeof(DWORD);
+ if (context->context.lcPktData & PK_Y)
+ rc += sizeof(DWORD);
+ if (context->context.lcPktData & PK_Z)
+ rc += sizeof(DWORD);
+ if (context->context.lcPktData & PK_NORMAL_PRESSURE)
+ rc += sizeof(UINT);
+ if (context->context.lcPktData & PK_TANGENT_PRESSURE)
+ rc += sizeof(UINT);
+ if (context->context.lcPktData & PK_ORIENTATION)
+ rc += sizeof(ORIENTATION);
+ if (context->context.lcPktData & PK_ROTATION)
+ rc += sizeof(ROTATION);
+
+ rc *= n;
+ memset(lpPkt,0,rc);
+}
+
+
/***********************************************************************
* WTInfoA (WINTAB32.20)
*/
UINT WINAPI WTInfoA(UINT wCategory, UINT nIndex, LPVOID lpOutput)
{
- FIXME("(%u, %u, %p): stub\n", wCategory, nIndex, lpOutput);
+ if (gLoaded == FALSE)
+ LoadTablet();
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
- return 0;
+ return pWTInfoA( wCategory, nIndex, lpOutput );
}
/***********************************************************************
@@ -61,11 +373,38 @@
*/
HCTX WINAPI WTOpenA(HWND hWnd, LPLOGCONTEXTA lpLogCtx, BOOL fEnable)
{
- FIXME("(%p, %p, %u): stub\n", hWnd, lpLogCtx, fEnable);
+ LPOPENCONTEXT newcontext;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %p, %u)\n", hWnd, lpLogCtx, fEnable);
+ DUMPCONTEXT(*lpLogCtx);
- return NULL;
+ newcontext = HeapAlloc(GetProcessHeap(), 0 , sizeof(OPENCONTEXT));
+ memcpy(&(newcontext->context),lpLogCtx,sizeof(LOGCONTEXTA));
+ newcontext->hwndOwner = hWnd;
+ newcontext->enabled = fEnable;
+ newcontext->ActiveCursor = -1;
+ newcontext->QueueSize = 10;
+ newcontext->PacketsQueued = 0;
+ newcontext->PacketQueue=HeapAlloc(GetProcessHeap(),0,sizeof(WTPACKET)*10);
+ newcontext->handle = gTopContext++;
+ newcontext->next = gOpenContexts;
+
+ EnterCriticalSection(&csTablet);
+ gOpenContexts = newcontext;
+ LeaveCriticalSection(&csTablet);
+
+ pAttachEventQueueToTablet(hWnd);
+
+ TABLET_PostTabletMessage(newcontext, WT_CTXOPEN, (WPARAM)newcontext->handle,
+ newcontext->context.lcStatus, TRUE);
+
+ newcontext->context.lcStatus = CXS_ONTOP;
+
+ TABLET_PostTabletMessage(newcontext, WT_CTXOVERLAP,
+ (WPARAM)newcontext->handle,
+ newcontext->context.lcStatus, TRUE);
+
+ return newcontext->handle;
}
/***********************************************************************
@@ -85,9 +424,32 @@
*/
BOOL WINAPI WTClose(HCTX hCtx)
{
- FIXME("(%p): stub\n", hCtx);
+ LPOPENCONTEXT context,ptr;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p)\n", hCtx);
+
+ ptr = context = gOpenContexts;
+
+ while ((context->handle != hCtx) && (context))
+ {
+ ptr = context;
+ context = context->next;
+ }
+ if (!context)
+ return TRUE;
+
+ EnterCriticalSection(&csTablet);
+ if (context == gOpenContexts)
+ gOpenContexts = context->next;
+ else
+ ptr->next = context->next;
+ LeaveCriticalSection(&csTablet);
+
+ TABLET_PostTabletMessage(context, WT_CTXCLOSE, (WPARAM)context->handle,
+ context->context.lcStatus,TRUE);
+
+ HeapFree(GetProcessHeap(),0,context->PacketQueue);
+ HeapFree(GetProcessHeap(),0,context);
return TRUE;
}
@@ -97,23 +459,74 @@
*/
int WINAPI WTPacketsGet(HCTX hCtx, int cMaxPkts, LPVOID lpPkts)
{
- FIXME("(%p, %d, %p): stub\n", hCtx, cMaxPkts, lpPkts);
+ int limit;
+ LPOPENCONTEXT context;
+ LPVOID ptr = lpPkts;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %d, %p)\n", hCtx, cMaxPkts, lpPkts);
+ if (!hCtx || !lpPkts) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+ TABLET_BlankPacketData(context,lpPkts,cMaxPkts);
+
+ EnterCriticalSection(&csTablet);
+ if (context->PacketsQueued == 0)
+ {
+ LeaveCriticalSection(&csTablet);
return 0;
}
+ for(limit = 0; limit < cMaxPkts && limit < context->PacketsQueued; limit++)
+ ptr=TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[limit]);
+
+ if (limit < context->PacketsQueued)
+ {
+ memcpy(context->PacketQueue, &context->PacketQueue[limit],
+ (context->QueueSize - (limit))*sizeof(WTPACKET));
+ }
+ context->PacketsQueued -= limit;
+ LeaveCriticalSection(&csTablet);
+
+ TRACE("Copied %i packets\n",limit);
+
+ return limit;
+}
+
/***********************************************************************
* WTPacket (WINTAB32.24)
*/
BOOL WINAPI WTPacket(HCTX hCtx, UINT wSerial, LPVOID lpPkt)
{
- FIXME("(%p, %d, %p): stub\n", hCtx, wSerial, lpPkt);
+ int rc = 0;
+ LPOPENCONTEXT context;
+ LPWTPACKET wtp;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %d, %p)\n", hCtx, wSerial, lpPkt);
- return FALSE;
+ if (!hCtx) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+
+ EnterCriticalSection(&csTablet);
+ rc = TABLET_FindPacket(context ,wSerial, &wtp);
+
+ if (rc >= 0)
+ {
+ if (lpPkt)
+ TABLET_CopyPacketData(context ,lpPkt, wtp);
+
+ if ((rc+1) < context->QueueSize)
+ {
+ memcpy(context->PacketQueue, &context->PacketQueue[rc+1],
+ (context->QueueSize - (rc+1))*sizeof(WTPACKET));
+ }
+ context->PacketsQueued -= (rc+1);
+ }
+ LeaveCriticalSection(&csTablet);
+
+ TRACE("Returning %i\n",rc+1);
+ return rc+1;
}
/***********************************************************************
@@ -121,11 +534,16 @@
*/
BOOL WINAPI WTEnable(HCTX hCtx, BOOL fEnable)
{
- FIXME("(%p, %u): stub\n", hCtx, fEnable);
+ LPOPENCONTEXT context;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %u)\n", hCtx, fEnable);
- return FALSE;
+ if (!hCtx) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+ context->enabled = fEnable;
+
+ return TRUE;
}
/***********************************************************************
@@ -135,9 +553,7 @@
{
FIXME("(%p, %u): stub\n", hCtx, fToTop);
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
- return FALSE;
+ return TRUE;
}
/***********************************************************************
@@ -157,11 +573,17 @@
*/
BOOL WINAPI WTGetA(HCTX hCtx, LPLOGCONTEXTA lpLogCtx)
{
- FIXME("(%p, %p): stub\n", hCtx, lpLogCtx);
+ LPOPENCONTEXT context;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %p)\n", hCtx, lpLogCtx);
- return FALSE;
+ if (!hCtx) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+
+ memcpy(lpLogCtx,&context->context,sizeof(LOGCONTEXTA));
+
+ return TRUE;
}
/***********************************************************************
@@ -253,49 +675,166 @@
*/
int WINAPI WTPacketsPeek(HCTX hCtx, int cMaxPkts, LPVOID lpPkts)
{
- FIXME("(%p, %d, %p): stub\n", hCtx, cMaxPkts, lpPkts);
+ int limit;
+ LPOPENCONTEXT context;
+ LPVOID ptr = lpPkts;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %d, %p)\n", hCtx, cMaxPkts, lpPkts);
+
+ if (!hCtx || !lpPkts) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+ EnterCriticalSection(&csTablet);
+ if (context->PacketsQueued == 0)
+ {
+ LeaveCriticalSection(&csTablet);
return 0;
}
+ for (limit = 0; limit < cMaxPkts && limit < context->PacketsQueued; limit++)
+ ptr = TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[limit]);
+
+ LeaveCriticalSection(&csTablet);
+ TRACE("Copied %i packets\n",limit);
+ return limit;
+}
+
/***********************************************************************
* WTDataGet (WINTAB32.81)
*/
int WINAPI WTDataGet(HCTX hCtx, UINT wBegin, UINT wEnd,
int cMaxPkts, LPVOID lpPkts, LPINT lpNPkts)
{
- FIXME("(%p, %u, %u, %d, %p, %p): stub\n",
+ LPOPENCONTEXT context;
+ LPVOID ptr = lpPkts;
+ UINT bgn = 0;
+ UINT end = 0;
+ UINT num = 0;
+
+ TRACE("(%p, %u, %u, %d, %p, %p)\n",
hCtx, wBegin, wEnd, cMaxPkts, lpPkts, lpNPkts);
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ if (!hCtx) return 0;
+ context = TABLET_FindOpenContext(hCtx);
+
+ EnterCriticalSection(&csTablet);
+ if (context->PacketsQueued == 0)
+ {
+ LeaveCriticalSection(&csTablet);
return 0;
}
+ while (bgn < context->PacketsQueued &&
+ context->PacketQueue[bgn].pkSerialNumber != wBegin)
+ bgn++;
+
+ end = bgn;
+ while (end < context->PacketsQueued &&
+ context->PacketQueue[end].pkSerialNumber != wEnd)
+ end++;
+
+ if (bgn == end == context->PacketsQueued)
+ {
+ LeaveCriticalSection(&csTablet);
+ return 0;
+ }
+
+ for (num = bgn; num <= end; num++)
+ ptr = TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[end]);
+
+ /* remove read packets */
+ if ((end+1) < context->PacketsQueued)
+ memcpy( &context->PacketQueue[bgn], &context->PacketQueue[end+1],
+ (context->PacketsQueued - ((end-bgn)+1)) * sizeof (WTPACKET));
+
+ context->PacketsQueued -= ((end-bgn)+1);
+ *lpNPkts = ((end-bgn)+1);
+
+ LeaveCriticalSection(&csTablet);
+ TRACE("Copied %i packets\n",*lpNPkts);
+ return (end - bgn)+1;
+}
+
/***********************************************************************
* WTDataPeek (WINTAB32.82)
*/
int WINAPI WTDataPeek(HCTX hCtx, UINT wBegin, UINT wEnd,
int cMaxPkts, LPVOID lpPkts, LPINT lpNPkts)
{
- FIXME("(%p, %u, %u, %d, %p, %p): stub\n",
+ LPOPENCONTEXT context;
+ LPVOID ptr = lpPkts;
+ UINT bgn = 0;
+ UINT end = 0;
+ UINT num = 0;
+
+ TRACE("(%p, %u, %u, %d, %p, %p)\n",
hCtx, wBegin, wEnd, cMaxPkts, lpPkts, lpNPkts);
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ if (!hCtx) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+
+ EnterCriticalSection(&csTablet);
+ if (context->PacketsQueued == 0)
+ {
+ LeaveCriticalSection(&csTablet);
+ return 0;
+ }
+ while (bgn < context->PacketsQueued &&
+ context->PacketQueue[bgn].pkSerialNumber != wBegin)
+ bgn++;
+
+ end = bgn;
+ while (end < context->PacketsQueued &&
+ context->PacketQueue[end].pkSerialNumber != wEnd)
+ end++;
+
+ if (bgn == context->PacketsQueued || end == context->PacketsQueued)
+ {
+ TRACE("%i %i %i \n", bgn, end, context->PacketsQueued);
+ LeaveCriticalSection(&csTablet);
return 0;
}
+ for (num = bgn; num <= end; num++)
+ ptr = TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[end]);
+
+ *lpNPkts = ((end-bgn)+1);
+ LeaveCriticalSection(&csTablet);
+
+ TRACE("Copied %i packets\n",*lpNPkts);
+ return (end - bgn)+1;
+}
+
/***********************************************************************
* WTQueuePacketsEx (WINTAB32.200)
*/
BOOL WINAPI WTQueuePacketsEx(HCTX hCtx, UINT *lpOld, UINT *lpNew)
{
- FIXME("(%p, %p, %p): stub\n", hCtx, lpOld, lpNew);
+ LPOPENCONTEXT context;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %p, %p)\n", hCtx, lpOld, lpNew);
+
+ if (!hCtx) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+
+ EnterCriticalSection(&csTablet);
+ if (context->PacketsQueued)
+ {
+ *lpOld = context->PacketQueue[0].pkSerialNumber;
+ *lpNew = context->PacketQueue[context->PacketsQueued-1].pkSerialNumber;
+ }
+ else
+ {
+ TRACE("No packets\n");
+ LeaveCriticalSection(&csTablet);
+ return FALSE;
+ }
+ LeaveCriticalSection(&csTablet);
return TRUE;
}
@@ -305,11 +844,13 @@
*/
int WINAPI WTQueueSizeGet(HCTX hCtx)
{
- FIXME("(%p): stub\n", hCtx);
+ LPOPENCONTEXT context;
+ TRACE("(%p)\n", hCtx);
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ if (!hCtx) return 0;
- return 0;
+ context = TABLET_FindOpenContext(hCtx);
+ return context->QueueSize;
}
/***********************************************************************
@@ -317,9 +858,20 @@
*/
BOOL WINAPI WTQueueSizeSet(HCTX hCtx, int nPkts)
{
- FIXME("(%p, %d): stub\n", hCtx, nPkts);
+ LPOPENCONTEXT context;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ TRACE("(%p, %d)\n", hCtx, nPkts);
- return 0;
+ if (!hCtx) return 0;
+
+ context = TABLET_FindOpenContext(hCtx);
+
+ EnterCriticalSection(&csTablet);
+ context->PacketQueue = HeapReAlloc(GetProcessHeap(), 0,
+ context->PacketQueue, sizeof(WTPACKET)*nPkts);
+
+ context->QueueSize = nPkts;
+ LeaveCriticalSection(&csTablet);
+
+ return nPkts;
}
--- /dev/null Thu Aug 30 15:30:55 2001
+++ dlls/wintab32/wintab32.c Thu Jan 8 08:47:13 2004
@@ -0,0 +1,155 @@
+/*
+ * WinTab32 library
+ *
+ * Copyright 2003 CodeWeavers, Aric Stewart
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "wintab.h"
+#include "wintab_internal.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wintab32);
+
+HWND hwndDefault = (HWND)NULL;
+static HANDLE hTabletInst;
+static const WCHAR
+WC_TABLETCLASSNAME[] = {'W','i','n','e','T','a','b','l','e','t','C','l','a','s','s',0};
+CRITICAL_SECTION csTablet;
+
+UINT (*pWTInfoA)(UINT wCategory, UINT nIndex, LPVOID lpOutput) = NULL;
+
+static LRESULT WINAPI TABLET_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam);
+
+static VOID TABLET_Register()
+{
+ WNDCLASSW wndClass;
+ ZeroMemory(&wndClass, sizeof(WNDCLASSW));
+ wndClass.style = CS_GLOBALCLASS;
+ wndClass.lpfnWndProc = (WNDPROC) TABLET_WindowProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hCursor = (HCURSOR)NULL;
+ wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW +1);
+ wndClass.lpszClassName = WC_TABLETCLASSNAME;
+ RegisterClassW(&wndClass);
+}
+
+static VOID TABLET_Unregister()
+{
+ UnregisterClassW(WC_TABLETCLASSNAME, (HINSTANCE)NULL);
+}
+
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved)
+{
+ static const WCHAR name[] = {'T','a','b','l','e','t',0};
+ static HMODULE hx11drv = NULL;
+
+ TRACE("%p, %lx, %p\n",hInstDLL,fdwReason,lpReserved);
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ TRACE("Initialization\n");
+ InitializeCriticalSection(&csTablet);
+ hx11drv = LoadLibraryA("x11drv.dll");
+ if (hx11drv)
+ {
+ pLoadTabletInfo = (X11_LoadTabletInfo*)GetProcAddress(hx11drv,
+ "LoadTabletInfo");
+ pAttachEventQueueToTablet = (X11_AttachEventQueueToTablet*)
+ GetProcAddress(hx11drv, "AttachEventQueueToTablet");
+ pGetCurrentPacket = (X11_GetCurrentPacket*)
+ GetProcAddress(hx11drv, "GetCurrentPacket");
+ pWTInfoA = (void *)GetProcAddress(hx11drv, "WTInfoA");
+ TABLET_Register();
+ hTabletInst = hInstDLL;
+ hwndDefault = CreateWindowW(WC_TABLETCLASSNAME, name,
+ WS_POPUPWINDOW,0,0,0,0,0,0,hTabletInst,0);
+ }
+ else
+ return FALSE;
+ break;
+ case DLL_PROCESS_DETACH:
+ TRACE("Detaching\n");
+ if (hx11drv)
+ {
+ FreeLibrary(hx11drv);
+ if (hwndDefault)
+ {
+ DestroyWindow(hwndDefault);
+ hwndDefault = 0;
+ }
+ TABLET_Unregister();
+ }
+ DeleteCriticalSection(&csTablet);
+ break;
+ }
+ return TRUE;
+}
+
+
+/*
+ * The window proc for the default TABLET window
+ */
+static LRESULT WINAPI TABLET_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam)
+{
+ TRACE("Incomming Message 0x%x (0x%08x, 0x%08x)\n", uMsg, (UINT)wParam,
+ (UINT)lParam);
+
+ switch(uMsg)
+ {
+ case WM_NCCREATE:
+ return TRUE;
+
+ case WT_PACKET:
+ {
+ WTPACKET packet;
+ LPOPENCONTEXT handler;
+ pGetCurrentPacket(&packet);
+ handler = AddPacketToContextQueue(&packet,(HWND)lParam);
+ if (handler)
+ TABLET_PostTabletMessage(handler, WT_PACKET,
+ (WPARAM)packet.pkSerialNumber,
+ (LPARAM)handler->handle, FALSE);
+ break;
+ }
+ case WT_PROXIMITY:
+ {
+ LPOPENCONTEXT handler;
+ LPARAM prox;
+ handler = FindOpenContext((HWND)lParam);
+ if (handler)
+ {
+ prox = wParam | 0x1 << 16;
+ TABLET_PostTabletMessage(handler, WT_PROXIMITY,
+ (WPARAM)handler->handle, (LPARAM)prox,
+ TRUE);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
--- /dev/null Thu Aug 30 15:30:55 2001
+++ dlls/wintab32/wintab_internal.h Thu Jan 8 08:47:13 2004
@@ -0,0 +1,163 @@
+/*
+ * Tablet header
+ *
+ * Copyright 2003 CodeWeavers (Aric Stewart)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+typedef struct tagWTI_INTERFACE_INFO {
+ CHAR WINTABID[1024];
+ /* a copy of the null-terminated tablet hardware identification string
+ * in the user buffer. This string should include make, model, and
+ * revision information in user-readable format.
+ */
+ WORD SPECVERSION;
+ /* the specification version number. The high-order byte contains the
+ * major version number; the low-order byte contains the minor version
+ * number.
+ */
+ WORD IMPLVERSION;
+ /* the implementation version number. The high-order byte contains the
+ * major version number; the low-order byte contains the minor version
+ * number.
+ */
+ UINT NDEVICES;
+ /* the number of devices supported. */
+ UINT NCURSORS;
+ /* the total number of cursor types supported. */
+ UINT NCONTEXTS;
+ /* the number of contexts supported. */
+ UINT CTXOPTIONS;
+ /* flags indicating which context options are supported */
+ UINT CTXSAVESIZE;
+ /* the size of the save information returned from WTSave.*/
+ UINT NEXTENSIONS;
+ /* the number of extension data items supported.*/
+ UINT NMANAGERS;
+ /* the number of manager handles supported.*/
+ }WTI_INTERFACE_INFO, *LPWTI_INTERFACE_INFO;
+
+typedef struct tagWTI_STATUS_INFO{
+ UINT CONTEXTS;
+ /* the number of contexts currently open.*/
+ UINT SYSCTXS;
+ /* the number of system contexts currently open.*/
+ UINT PKTRATE;
+ /* the maximum packet report rate currently being received by any
+ * context, in Hertz.
+ */
+ WTPKT PKTDATA;
+ /* a mask indicating which packet data items are requested by at
+ * least one context.
+ */
+ UINT MANAGERS;
+ /* the number of manager handles currently open.*/
+ BOOL SYSTEM;
+ /* a non-zero value if system pointing is available to the whole
+ * screen; zero otherwise.
+ */
+ DWORD BUTTONUSE;
+ /* a button mask indicating the logical buttons whose events are
+ * requested by at least one context.
+ */
+ DWORD SYSBTNUSE;
+ /* a button mask indicating which logical buttons are assigned a system
+ * button function by the current cursor's system button map.
+ */
+} WTI_STATUS_INFO, *LPWTI_STATUS_INFO;
+
+typedef struct tagWTI_EXTENSIONS_INFO
+{
+ CHAR NAME[256];
+ /* a unique, null-terminated string describing the extension.*/
+ UINT TAG;
+ /* a unique identifier for the extension. */
+ WTPKT MASK;
+ /* a mask that can be bitwise OR'ed with WTPKT-type variables to select
+ * the extension.
+ */
+ UINT SIZE[2];
+ /* an array of two UINTs specifying the extension's size within a packet
+ * (in bytes). The first is for absolute mode; the second is for
+ * relative mode.
+ */
+ AXIS *AXES;
+ /* an array of axis descriptions, as needed for the extension. */
+ BYTE *DEFAULT;
+ /* the current global default data, as needed for the extension. This
+ * data is modified via the WTMgrExt function.
+ */
+ BYTE *DEFCONTEXT;
+ BYTE *DEFSYSCTX;
+ /* the current default context-specific data, as needed for the
+ * extension. The indices identify the digitizing- and system-context
+ * defaults, respectively.
+ */
+ BYTE *CURSORS;
+ /* Is the first of one or more consecutive indices, one per cursor type.
+ * Each returns the current default cursor-specific data, as need for
+ * the extension. This data is modified via the WTMgrCsrExt function.
+ */
+} WTI_EXTENSIONS_INFO, *LPWTI_EXTENSIONS_INFO;
+
+typedef struct tagWTPACKET {
+ HCTX pkContext;
+ UINT pkStatus;
+ LONG pkTime;
+ WTPKT pkChanged;
+ UINT pkSerialNumber;
+ UINT pkCursor;
+ DWORD pkButtons;
+ DWORD pkX;
+ DWORD pkY;
+ DWORD pkZ;
+ UINT pkNormalPressure;
+ UINT pkTangentPressure;
+ ORIENTATION pkOrientation;
+ ROTATION pkRotation; /* 1.1 */
+} WTPACKET, *LPWTPACKET;
+
+typedef struct tagOPENCONTEXT
+{
+ HCTX handle;
+ LOGCONTEXTA context;
+ HWND hwndOwner;
+ BOOL enabled;
+ INT ActiveCursor;
+ INT QueueSize;
+ INT PacketsQueued;
+ LPWTPACKET PacketQueue;
+ struct tagOPENCONTEXT *next;
+} OPENCONTEXT, *LPOPENCONTEXT;
+
+LPOPENCONTEXT TABLET_FindOpenContext(HCTX hCtx);
+int TABLET_PostTabletMessage(LPOPENCONTEXT newcontext, UINT msg, WPARAM wParam,
+ LPARAM lParam, BOOL send_always);
+LPOPENCONTEXT AddPacketToContextQueue(LPWTPACKET packet, HWND hwnd);
+LPOPENCONTEXT FindOpenContext(HWND hwnd);
+
+/* X11drv functions */
+typedef int X11_LoadTabletInfo(HWND hwnddefault);
+
+typedef int X11_GetCurrentPacket(LPWTPACKET packet);
+
+typedef int X11_AttachEventQueueToTablet(HWND hOwner);
+
+X11_LoadTabletInfo *pLoadTabletInfo;
+X11_AttachEventQueueToTablet *pAttachEventQueueToTablet;
+X11_GetCurrentPacket *pGetCurrentPacket;
+
+extern UINT (*pWTInfoA)(UINT wCategory, UINT nIndex, LPVOID lpOutput);
Index: include/wintab.h
===================================================================
RCS file: /home/wine/wine/include/wintab.h,v
retrieving revision 1.1
diff -u -w -r1.1 wintab.h
--- include/wintab.h 17 Dec 2002 01:49:16 -0000 1.1
+++ include/wintab.h 8 Jan 2004 14:59:11 -0000
@@ -328,7 +328,9 @@
#define CSR_MINPKTDATA 17 /* 1.1 */
#define CSR_MINBUTTONS 18 /* 1.1 */
#define CSR_CAPABILITIES 19 /* 1.1 */
-#define CSR_MAX 19
+/* from http://www.wacomeng.com/devsupport/ibmpc/wacomwindevfaq.html */
+#define CSR_TYPE 20
+#define CSR_MAX 20
#endif
Index: dlls/x11drv/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/Makefile.in,v
retrieving revision 1.38
diff -u -w -r1.38 Makefile.in
--- dlls/x11drv/Makefile.in 5 Dec 2003 00:20:28 -0000 1.38
+++ dlls/x11drv/Makefile.in 8 Jan 2004 14:59:54 -0000
@@ -34,10 +34,12 @@
text.c \
window.c \
winpos.c \
+ wintab.c \
x11ddraw.c \
x11drv_main.c \
xdnd.c \
Index: dlls/x11drv/event.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/event.c,v
retrieving revision 1.30
diff -u -w -r1.30 event.c
--- dlls/x11drv/event.c 5 Dec 2003 04:45:50 -0000 1.30
+++ dlls/x11drv/event.c 8 Jan 2004 14:59:54 -0000
@@ -275,8 +277,18 @@
WARN( "Got event %s for unknown Window %08lx\n",
event_names[event->type], event->xany.window );
else
- TRACE("Got event %s for hwnd %p\n",
- event_names[event->type], hWnd );
+ if (event->type <= MappingNotify)
+ TRACE("Got event %s for hwnd/window %p/%lx, GetFocus()=%p\n",
+ event_names[event->type], hWnd, event->xany.window, GetFocus() );
+ else
+ TRACE("Got extension event for hwnd/window %p/%lx, GetFocus()=%p\n",
+ hWnd, event->xany.window, GetFocus() );
+
+ if (X11DRV_ProcessTabletEvent(hWnd, event))
+ {
+ TRACE("Return: filtered by tablet\n");
+ return;
+ }
switch(event->type)
{
RCS file: /home/wine/wine/dlls/x11drv/x11drv.spec,v
retrieving revision 1.50
diff -u -w -r1.50 x11drv.spec
--- dlls/x11drv/x11drv.spec 26 Nov 2003 22:03:34 -0000 1.50
+++ dlls/x11drv/x11drv.spec 8 Jan 2004 14:59:57 -0000
@@ -114,3 +114,12 @@
# X11 locks
@ cdecl -norelay wine_tsx11_lock()
@ cdecl -norelay wine_tsx11_unlock()
+
+# WinTab32
+@ cdecl LoadTabletInfo(long) X11DRV_LoadTabletInfo
+@ cdecl AttachEventQueueToTablet(long) X11DRV_AttachEventQueueToTablet
+@ cdecl GetCurrentPacket(ptr) X11DRV_GetCurrentPacket
+@ cdecl WTInfoA(long long ptr) X11DRV_WTInfoA
Index: dlls/x11drv/x11drv_main.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/x11drv_main.c,v
retrieving revision 1.83
diff -u -w -r1.83 x11drv_main.c
--- dlls/x11drv/x11drv_main.c 5 Dec 2003 00:11:48 -0000 1.83
+++ dlls/x11drv/x11drv_main.c 8 Jan 2004 14:59:57 -0000
@@ -62,7 +62,10 @@
#include "xrandr.h"
#include "dga2.h"
#include "wine/server.h"
+#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
--- /dev/null Thu Aug 30 15:30:55 2001
+++ dlls/x11drv/wintab.c Thu Jan 8 08:47:17 2004
@@ -0,0 +1,1134 @@
+/*
+ * X11 tablet driver
+ *
+ * Copyright 2003 CodeWeavers (Aric Stewart)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XInput.h>
+#include <X11/extensions/XI.h>
+
+#include "windef.h"
+#include "x11drv.h"
+#include "wine/debug.h"
+#include "wintab.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wintab32);
+WINE_DECLARE_DEBUG_CHANNEL(event);
+
+
+typedef struct tagWTI_CURSORS_INFO
+{
+ CHAR NAME[256];
+ /* a displayable zero-terminated string containing the name of the
+ * cursor.
+ */
+ BOOL ACTIVE;
+ /* whether the cursor is currently connected. */
+ WTPKT PKTDATA;
+ /* a bit mask indicating the packet data items supported when this
+ * cursor is connected.
+ */
+ BYTE BUTTONS;
+ /* the number of buttons on this cursor. */
+ BYTE BUTTONBITS;
+ /* the number of bits of raw button data returned by the hardware.*/
+ CHAR BTNNAMES[1024]; /* FIXME: make this dynamic */
+ /* a list of zero-terminated strings containing the names of the
+ * cursor's buttons. The number of names in the list is the same as the
+ * number of buttons on the cursor. The names are separated by a single
+ * zero character; the list is terminated by two zero characters.
+ */
+ BYTE BUTTONMAP[32];
+ /* a 32 byte array of logical button numbers, one for each physical
+ * button.
+ */
+ BYTE SYSBTNMAP[32];
+ /* a 32 byte array of button action codes, one for each logical
+ * button.
+ */
+ BYTE NPBUTTON;
+ /* the physical button number of the button that is controlled by normal
+ * pressure.
+ */
+ UINT NPBTNMARKS[2];
+ /* an array of two UINTs, specifying the button marks for the normal
+ * pressure button. The first UINT contains the release mark; the second
+ * contains the press mark.
+ */
+ UINT *NPRESPONSE;
+ /* an array of UINTs describing the pressure response curve for normal
+ * pressure.
+ */
+ BYTE TPBUTTON;
+ /* the physical button number of the button that is controlled by
+ * tangential pressure.
+ */
+ UINT TPBTNMARKS[2];
+ /* an array of two UINTs, specifying the button marks for the tangential
+ * pressure button. The first UINT contains the release mark; the second
+ * contains the press mark.
+ */
+ UINT *TPRESPONSE;
+ /* an array of UINTs describing the pressure response curve for
+ * tangential pressure.
+ */
+ DWORD PHYSID;
+ /* a manufacturer-specific physical identifier for the cursor. This
+ * value will distinguish the physical cursor from others on the same
+ * device. This physical identifier allows applications to bind
+ * functions to specific physical cursors, even if category numbers
+ * change and multiple, otherwise identical, physical cursors are
+ * present.
+ */
+ UINT MODE;
+ /* the cursor mode number of this cursor type, if this cursor type has
+ * the CRC_MULTIMODE capability.
+ */
+ UINT MINPKTDATA;
+ /* the minimum set of data available from a physical cursor in this
+ * cursor type, if this cursor type has the CRC_AGGREGATE capability.
+ */
+ UINT MINBUTTONS;
+ /* the minimum number of buttons of physical cursors in the cursor type,
+ * if this cursor type has the CRC_AGGREGATE capability.
+ */
+ UINT CAPABILITIES;
+ /* flags indicating cursor capabilities, as defined below:
+ CRC_MULTIMODE
+ Indicates this cursor type describes one of several modes of a
+ single physical cursor. Consecutive cursor type categories
+ describe the modes; the CSR_MODE data item gives the mode number
+ of each cursor type.
+ CRC_AGGREGATE
+ Indicates this cursor type describes several physical cursors
+ that cannot be distinguished by software.
+ CRC_INVERT
+ Indicates this cursor type describes the physical cursor in its
+ inverted orientation; the previous consecutive cursor type
+ category describes the normal orientation.
+ */
+ UINT TYPE;
+ /* Manufacturer Unique id for the item type */
+} WTI_CURSORS_INFO, *LPWTI_CURSORS_INFO;
+
+
+typedef struct tagWTI_DEVICES_INFO
+{
+ CHAR NAME[256];
+ /* a displayable null- terminated string describing the device,
+ * manufacturer, and revision level.
+ */
+ UINT HARDWARE;
+ /* flags indicating hardware and driver capabilities, as defined
+ * below:
+ HWC_INTEGRATED:
+ Indicates that the display and digitizer share the same surface.
+ HWC_TOUCH
+ Indicates that the cursor must be in physical contact with the
+ device to report position.
+ HWC_HARDPROX
+ Indicates that device can generate events when the cursor is
+ entering and leaving the physical detection range.
+ HWC_PHYSID_CURSORS
+ Indicates that device can uniquely identify the active cursor in
+ hardware.
+ */
+ UINT NCSRTYPES;
+ /* the number of supported cursor types.*/
+ UINT FIRSTCSR;
+ /* the first cursor type number for the device. */
+ UINT PKTRATE;
+ /* the maximum packet report rate in Hertz. */
+ WTPKT PKTDATA;
+ /* a bit mask indicating which packet data items are always available.*/
+ WTPKT PKTMODE;
+ /* a bit mask indicating which packet data items are physically
+ * relative, i.e., items for which the hardware can only report change,
+ * not absolute measurement.
+ */
+ WTPKT CSRDATA;
+ /* a bit mask indicating which packet data items are only available when
+ * certain cursors are connected. The individual cursor descriptions
+ * must be consulted to determine which cursors return which data.
+ */
+ INT XMARGIN;
+ INT YMARGIN;
+ INT ZMARGIN;
+ /* the size of tablet context margins in tablet native coordinates, in
+ * the x, y, and z directions, respectively.
+ */
+ AXIS X;
+ AXIS Y;
+ AXIS Z;
+ /* the tablet's range and resolution capabilities, in the x, y, and z
+ * axes, respectively.
+ */
+ AXIS NPRESSURE;
+ AXIS TPRESSURE;
+ /* the tablet's range and resolution capabilities, for the normal and
+ * tangential pressure inputs, respectively.
+ */
+ AXIS ORIENTATION[3];
+ /* a 3-element array describing the tablet's orientation range and
+ * resolution capabilities.
+ */
+ AXIS ROTATION[3];
+ /* a 3-element array describing the tablet's rotation range and
+ * resolution capabilities.
+ */
+ CHAR PNPID[256];
+ /* a null-terminated string containing the devices Plug and Play ID.*/
+} WTI_DEVICES_INFO, *LPWTI_DEVICES_INFO;
+
+typedef struct tagWTPACKET {
+ HCTX pkContext;
+ UINT pkStatus;
+ LONG pkTime;
+ WTPKT pkChanged;
+ UINT pkSerialNumber;
+ UINT pkCursor;
+ DWORD pkButtons;
+ DWORD pkX;
+ DWORD pkY;
+ DWORD pkZ;
+ UINT pkNormalPressure;
+ UINT pkTangentPressure;
+ ORIENTATION pkOrientation;
+ ROTATION pkRotation; /* 1.1 */
+} WTPACKET, *LPWTPACKET;
+
+
+static int motion_type = -1;
+static int button_press_type = -1;
+static int button_release_type = -1;
+static int key_press_type = -1;
+static int key_release_type = -1;
+static int proximity_in_type = -1;
+static int proximity_out_type = -1;
+
+HWND hwndTabletDefault = (HWND)NULL;
+WTPACKET gMsgPacket;
+static DWORD gSerial = 0;
+static INT button_state[10] = {0,0,0,0,0,0,0,0,0,0};
+
+#define CURSORMAX 10
+
+static LOGCONTEXTA gSysContext;
+static WTI_DEVICES_INFO gSysDevice;
+static WTI_CURSORS_INFO gSysCursor[CURSORMAX];
+static INT gNumCursors;
+
+
+/* XInput stuff */
+static void *xinput_handle;
+
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f;
+MAKE_FUNCPTR(XListInputDevices)
+MAKE_FUNCPTR(XOpenDevice)
+MAKE_FUNCPTR(XQueryDeviceState)
+MAKE_FUNCPTR(XGetDeviceButtonMapping)
+MAKE_FUNCPTR(XCloseDevice)
+MAKE_FUNCPTR(XSelectExtensionEvent)
+MAKE_FUNCPTR(XFreeDeviceState)
+#undef MAKE_FUNCPTR
+
+INT X11DRV_XInput_Init(void)
+{
+ xinput_handle = wine_dlopen("libXi.so", RTLD_NOW, NULL, 0);
+ if (xinput_handle)
+ {
+#define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xinput_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
+ LOAD_FUNCPTR(XListInputDevices)
+ LOAD_FUNCPTR(XOpenDevice)
+ LOAD_FUNCPTR(XGetDeviceButtonMapping)
+ LOAD_FUNCPTR(XCloseDevice)
+ LOAD_FUNCPTR(XSelectExtensionEvent)
+ LOAD_FUNCPTR(XQueryDeviceState)
+ LOAD_FUNCPTR(XFreeDeviceState)
+#undef LOAD_FUNCPTR
+ return 1;
+ }
+sym_not_found:
+ return 0;
+}
+
+void X11DRV_LoadTabletInfo(HWND hwnddefault)
+{
+ struct x11drv_thread_data *data = x11drv_thread_data();
+ int num_devices;
+ int loop;
+ int cursor_target;
+ XDeviceInfo *devices;
+ XDeviceInfo *target = NULL;
+ BOOL axis_read_complete= FALSE;
+
+ XAnyClassPtr any;
+ XButtonInfoPtr Button;
+ XValuatorInfoPtr Val;
+ XAxisInfoPtr Axis;
+
+ XDevice *opendevice;
+
+ if (!X11DRV_XInput_Init())
+ {
+ ERR("Unable to initialized the XInput library.\n");
+ return;
+ }
+
+ hwndTabletDefault = hwnddefault;
+
+ /* Do base initializaion */
+ strcpy(gSysContext.lcName, "Wine Tablet Context");
+ strcpy(gSysDevice.NAME,"Wine Tablet Device");
+
+ gSysContext.lcOptions = CXO_SYSTEM | CXO_MESSAGES | CXO_CSRMESSAGES;
+ gSysContext.lcLocks = CXL_INSIZE | CXL_INASPECT | CXL_MARGIN |
+ CXL_SENSITIVITY | CXL_SYSOUT;
+
+ gSysContext.lcMsgBase= WT_DEFBASE;
+ gSysContext.lcDevice = 0;
+ gSysContext.lcPktData =
+ PK_CONTEXT | PK_STATUS | PK_SERIAL_NUMBER| PK_TIME | PK_CURSOR |
+ PK_BUTTONS | PK_X | PK_Y | PK_NORMAL_PRESSURE | PK_ORIENTATION;
+ gSysContext.lcMoveMask=
+ PK_BUTTONS | PK_X | PK_Y | PK_NORMAL_PRESSURE | PK_ORIENTATION;
+ gSysContext.lcStatus = CXS_ONTOP;
+ gSysContext.lcPktRate = 100;
+ gSysContext.lcBtnDnMask = 0xffffffff;
+ gSysContext.lcBtnUpMask = 0xffffffff;
+ gSysContext.lcSensX = 65536;
+ gSysContext.lcSensY = 65536;
+ gSysContext.lcSensX = 65536;
+ gSysContext.lcSensZ = 65536;
+ gSysContext.lcSysSensX= 65536;
+ gSysContext.lcSysSensY= 65536;
+
+ /* Device Defaults */
+ gSysDevice.HARDWARE = HWC_HARDPROX|HWC_PHYSID_CURSORS;
+ gSysDevice.FIRSTCSR= 0;
+ gSysDevice.PKTRATE = 100;
+ gSysDevice.PKTDATA =
+ PK_CONTEXT | PK_STATUS | PK_SERIAL_NUMBER| PK_TIME | PK_CURSOR |
+ PK_BUTTONS | PK_X | PK_Y | PK_NORMAL_PRESSURE | PK_ORIENTATION;
+ strcpy(gSysDevice.PNPID,"non-pluginplay");
+
+ wine_tsx11_lock();
+
+ cursor_target = -1;
+ devices = pXListInputDevices(data->display, &num_devices);
+ if (!devices)
+ {
+ WARN("XInput Extenstions reported as not avalable\n");
+ wine_tsx11_unlock();
+ return;
+ }
+ for (loop=0; loop < num_devices; loop++)
+ {
+ int class_loop;
+
+ TRACE("Trying device %i(%s)\n",loop,devices[loop].name);
+ if (devices[loop].use == IsXExtensionDevice)
+ {
+ LPWTI_CURSORS_INFO cursor;
+
+ TRACE("Is Extension Device\n");
+ cursor_target++;
+ target = &devices[loop];
+ cursor = &gSysCursor[cursor_target];
+
+ opendevice = pXOpenDevice(data->display,target->id);
+ if (opendevice)
+ {
+ unsigned char map[32];
+ int i;
+ int shft = 0;
+
+ pXGetDeviceButtonMapping(data->display, opendevice, map, 32);
+
+ for (i=0; i< cursor->BUTTONS; i++,shft++)
+ {
+ cursor->BUTTONMAP[i] = map[i];
+ cursor->SYSBTNMAP[i] = (1<<shft);
+ }
+ pXCloseDevice(data->display, opendevice);
+ }
+ else
+ {
+ WARN("Unable to open device %s\n",target->name);
+ cursor_target --;
+ continue;
+ }
+
+ strcpy(cursor->NAME,target->name);
+
+ cursor->ACTIVE = 1;
+ cursor->PKTDATA = PK_TIME | PK_CURSOR | PK_BUTTONS | PK_X | PK_Y |
+ PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE |
+ PK_ORIENTATION;
+
+ cursor->PHYSID = cursor_target;
+ cursor->NPBUTTON = 1;
+ cursor->NPBTNMARKS[0] = 0 ;
+ cursor->NPBTNMARKS[1] = 1 ;
+ cursor->CAPABILITIES = 1;
+ if (strcasecmp(cursor->NAME,"stylus")==0)
+ cursor->TYPE = 0x4825;
+ if (strcasecmp(cursor->NAME,"eraser")==0)
+ cursor->TYPE = 0xc85a;
+
+
+ any = (XAnyClassPtr) (target->inputclassinfo);
+
+ for (class_loop = 0; class_loop < target->num_classes; class_loop++)
+ {
+ switch (any->class)
+ {
+ case ValuatorClass:
+ if (!axis_read_complete)
+ {
+ Val = (XValuatorInfoPtr) any;
+ Axis = (XAxisInfoPtr) ((char *) Val + sizeof
+ (XValuatorInfo));
+
+ if (Val->num_axes>=1)
+ {
+ /* Axis 1 is X */
+ gSysDevice.X.axMin = Axis->min_value;
+ gSysDevice.X.axMax= Axis->max_value;
+ gSysDevice.X.axUnits = 1;
+ gSysDevice.X.axResolution = Axis->resolution;
+ gSysContext.lcInOrgX = Axis->min_value;
+ gSysContext.lcSysOrgX = Axis->min_value;
+ gSysContext.lcInExtX = Axis->max_value;
+ gSysContext.lcSysExtX = Axis->max_value;
+ Axis++;
+ }
+ if (Val->num_axes>=2)
+ {
+ /* Axis 2 is Y */
+ gSysDevice.Y.axMin = Axis->min_value;
+ gSysDevice.Y.axMax= Axis->max_value;
+ gSysDevice.Y.axUnits = 1;
+ gSysDevice.Y.axResolution = Axis->resolution;
+ gSysContext.lcInOrgY = Axis->min_value;
+ gSysContext.lcSysOrgY = Axis->min_value;
+ gSysContext.lcInExtY = Axis->max_value;
+ gSysContext.lcSysExtY = Axis->max_value;
+ Axis++;
+ }
+ if (Val->num_axes>=3)
+ {
+ /* Axis 3 is Normal Pressure */
+ gSysDevice.NPRESSURE.axMin = Axis->min_value;
+ gSysDevice.NPRESSURE.axMax= Axis->max_value;
+ gSysDevice.NPRESSURE.axUnits = 1;
+ gSysDevice.NPRESSURE.axResolution =
+ Axis->resolution;
+ Axis++;
+ }
+ if (Val->num_axes >= 5)
+ {
+ /* Axis 4 and 5 are X and Y tilt */
+ XAxisInfoPtr XAxis = Axis;
+ Axis++;
+ if (max (abs(Axis->max_value),
+ abs(XAxis->max_value)))
+ {
+ gSysDevice.ORIENTATION[0].axMin = 0;
+ gSysDevice.ORIENTATION[0].axMax = 3600;
+ gSysDevice.ORIENTATION[0].axUnits = 1;
+ gSysDevice.ORIENTATION[0].axResolution =
+ 235929600;
+ gSysDevice.ORIENTATION[1].axMin = -1000;
+ gSysDevice.ORIENTATION[1].axMax = 1000;
+ gSysDevice.ORIENTATION[1].axUnits = 1;
+ gSysDevice.ORIENTATION[1].axResolution =
+ 235929600;
+ Axis++;
+ }
+ }
+ axis_read_complete = TRUE;
+ }
+ break;
+ case ButtonClass:
+ {
+ CHAR *ptr = cursor->BTNNAMES;
+ int i;
+
+ Button = (XButtonInfoPtr) any;
+ cursor->BUTTONS = Button->num_buttons;
+ for (i = 0; i < cursor->BUTTONS; i++)
+ {
+ strcpy(ptr,cursor->NAME);
+ ptr+=8;
+ }
+ }
+ break;
+ }
+ any = (XAnyClassPtr) ((char*) any + any->length);
+ }
+ }
+ }
+ wine_tsx11_unlock();
+ gSysDevice.NCSRTYPES = cursor_target+1;
+ gNumCursors = cursor_target+1;
+}
+
+static int figure_deg(int x, int y)
+{
+ int rc;
+
+ if (y != 0)
+ {
+ rc = (int) 10 * (atan( (FLOAT)abs(y) / (FLOAT)abs(x)) / (3.1415 / 180));
+ if (y>0)
+ {
+ if (x>0)
+ rc += 900;
+ else
+ rc = 2700 - rc;
+ }
+ else
+ {
+ if (x>0)
+ rc = 900 - rc;
+ else
+ rc += 2700;
+ }
+ }
+ else
+ {
+ if (x >= 0)
+ rc = 900;
+ else
+ rc = 2700;
+ }
+
+ return rc;
+}
+
+static int get_button_state(int deviceid)
+{
+ return button_state[deviceid];
+}
+
+static void set_button_state(XID deviceid)
+{
+ struct x11drv_thread_data *data = x11drv_thread_data();
+ XDevice *device;
+ XDeviceState *state;
+ XInputClass *class;
+ int loop;
+ int rc = 0;
+
+ wine_tsx11_lock();
+ device = pXOpenDevice(data->display,deviceid);
+ state = pXQueryDeviceState(data->display,device);
+
+ if (state)
+ {
+ class = state->data;
+ for (loop = 0; loop < state->num_classes; loop++)
+ {
+ if (class->class == ButtonClass)
+ {
+ int loop2;
+ XButtonState *button_state = (XButtonState*)class;
+ for (loop2 = 1; loop2 <= button_state->num_buttons; loop2++)
+ {
+ if (button_state->buttons[loop2 / 8] & (1 << (loop2 % 8)))
+ {
+ rc |= (1<<(loop2-1));
+ }
+ }
+ }
+ class = (XInputClass *) ((char *) class + class->length);
+ }
+ }
+ pXFreeDeviceState(state);
+ wine_tsx11_unlock();
+ button_state[deviceid] = rc;
+}
+
+int X11DRV_ProcessTabletEvent(HWND hwnd, XEvent *event)
+{
+ memset(&gMsgPacket,0,sizeof(WTPACKET));
+
+ if(event->type == motion_type)
+ {
+ XDeviceMotionEvent *motion = (XDeviceMotionEvent *)event;
+
+ TRACE_(event)("Received tablet motion event (%p)\n",hwnd);
+ TRACE("Received tablet motion event (%p)\n",hwnd);
+ gMsgPacket.pkTime = motion->time;
+ gMsgPacket.pkSerialNumber = gSerial++;
+ gMsgPacket.pkCursor = motion->deviceid;
+ gMsgPacket.pkX = motion->axis_data[0];
+ gMsgPacket.pkY = motion->axis_data[1];
+ gMsgPacket.pkOrientation.orAzimuth =
+ figure_deg(motion->axis_data[3],motion->axis_data[4]);
+ gMsgPacket.pkOrientation.orAltitude = 1000 - 15 * max
+ (abs(motion->axis_data[3]),abs(motion->axis_data[4]));
+ gMsgPacket.pkNormalPressure = motion->axis_data[2];
+ gMsgPacket.pkButtons = get_button_state(motion->deviceid);
+ SendMessageW(hwndTabletDefault,WT_PACKET,0,(LPARAM)hwnd);
+ }
+ else if ((event->type == button_press_type)||(event->type ==
+ button_release_type))
+ {
+ XDeviceButtonEvent *button = (XDeviceButtonEvent *) event;
+
+ TRACE_(event)("Received tablet button event\n");
+ TRACE("Received tablet button %s event\n", (event->type ==
+ button_press_type)?"press":"release");
+
+ set_button_state(button->deviceid);
+ }
+ else if (event->type == key_press_type)
+ {
+ TRACE_(event)("Received tablet key press event\n");
+ FIXME("Received tablet key press event\n");
+ }
+ else if (event->type == key_release_type)
+ {
+ TRACE_(event)("Received tablet key release event\n");
+ FIXME("Received tablet key release event\n");
+ }
+ else if ((event->type == proximity_in_type) ||
+ (event->type == proximity_out_type))
+ {
+ TRACE_(event)("Received tablet proximity event\n");
+ TRACE("Received tablet proximity event\n");
+ gMsgPacket.pkStatus = (event->type==proximity_out_type)?TPS_PROXIMITY:0;
+ SendMessageW(hwndTabletDefault, WT_PROXIMITY,
+ (event->type==proximity_out_type)?0:1, (LPARAM)hwnd);
+ }
+ else
+ return 0;
+
+ return 1;
+}
+
+int X11DRV_AttachEventQueueToTablet(HWND hOwner)
+{
+ struct x11drv_thread_data *data = x11drv_thread_data();
+ int num_devices;
+ int loop;
+ int cur_loop;
+ XDeviceInfo *devices;
+ XDeviceInfo *target = NULL;
+ XDevice *the_device;
+ XInputClassInfo *ip;
+ XEventClass event_list[7];
+ Window win;
+ WND *wndPtr;
+
+ wine_tsx11_lock();
+
+ wndPtr = WIN_GetPtr(hOwner);
+ win = ((struct x11drv_win_data*)wndPtr->pDriverData)->whole_window;
+
+ TRACE("Creating context for window 0x%x (%lx) %i cursors\n",(INT)hOwner,
+ win, gNumCursors);
+
+ devices = pXListInputDevices(data->display, &num_devices);
+
+ for (cur_loop=0; cur_loop < gNumCursors; cur_loop++)
+ {
+ int event_number=0;
+
+ for (loop=0; loop < num_devices; loop ++)
+ if (strcmp(devices[loop].name,gSysCursor[cur_loop].NAME)==0)
+ target = &devices[loop];
+
+ TRACE("Opening cursor %i id %i\n",cur_loop,(INT)target->id);
+
+ the_device = pXOpenDevice(data->display, target->id);
+
+ if (!the_device)
+ {
+ WARN("Unable to Open device\n");
+ continue;
+ }
+
+ if (the_device->num_classes > 0)
+ {
+ for (ip = the_device->classes, loop=0; loop < target->num_classes;
+ ip++, loop++)
+ {
+ switch(ip->input_class)
+ {
+ case KeyClass:
+ DeviceKeyPress(the_device, key_press_type,
+ event_list[event_number]);
+ event_number++;
+ DeviceKeyRelease(the_device, key_release_type,
+ event_list[event_number]);
+ event_number++;
+ break;
+ case ButtonClass:
+ DeviceButtonPress(the_device, button_press_type,
+ event_list[event_number]);
+ event_number++;
+ DeviceButtonRelease(the_device, button_release_type,
+ event_list[event_number]);
+ event_number++;
+ break;
+ case ValuatorClass:
+ DeviceMotionNotify(the_device, motion_type,
+ event_list[event_number]);
+ event_number++;
+ ProximityIn(the_device, proximity_in_type,
+ event_list[event_number]);
+ event_number++;
+ ProximityOut(the_device, proximity_out_type,
+ event_list[event_number]);
+ event_number++;
+ break;
+ default:
+ ERR("unknown class\n");
+ break;
+ }
+ }
+ if (pXSelectExtensionEvent(data->display, win, event_list,
+ event_number))
+ {
+ ERR( "error selecting extended events\n");
+ goto end;
+ }
+ }
+ }
+
+end:
+ WIN_ReleasePtr(wndPtr);
+ wine_tsx11_unlock();
+ return 0;
+}
+
+int X11DRV_GetCurrentPacket(LPWTPACKET *packet)
+{
+ memcpy(packet,&gMsgPacket,sizeof(WTPACKET));
+ return 1;
+}
+
+
+int static inline CopyTabletData(LPVOID target, LPVOID src, INT size)
+{
+ memcpy(target,src,size);
+ return(size);
+}
+
+/***********************************************************************
+ * X11DRV_WTInfoA (X11DRV.@)
+ */
+UINT X11DRV_WTInfoA(UINT wCategory, UINT nIndex, LPVOID lpOutput)
+{
+ int rc = 0;
+ LPWTI_CURSORS_INFO tgtcursor;
+ TRACE("(%u, %u, %p)\n", wCategory, nIndex, lpOutput);
+
+ switch(wCategory)
+ {
+ case 0:
+ /* return largest necessary buffer */
+ TRACE("%i cursors\n",gNumCursors);
+ if (gNumCursors>0)
+ {
+ FIXME("Return proper size\n");
+ return 200;
+ }
+ else
+ return 0;
+ break;
+ case WTI_INTERFACE:
+ switch (nIndex)
+ {
+ WORD version;
+ case IFC_WINTABID:
+ strcpy(lpOutput,"Wine Wintab 1.1");
+ rc = 16;
+ break;
+ case IFC_SPECVERSION:
+ version = (0x01) | (0x01 << 8);
+ rc = CopyTabletData(lpOutput, &version,sizeof(WORD));
+ break;
+ case IFC_IMPLVERSION:
+ version = (0x00) | (0x01 << 8);
+ rc = CopyTabletData(lpOutput, &version,sizeof(WORD));
+ break;
+ default:
+ FIXME("WTI_INTERFACE unhandled index %i\n",nIndex);
+ rc = 0;
+
+ }
+ case WTI_DEFSYSCTX:
+ case WTI_DDCTXS:
+ case WTI_DEFCONTEXT:
+ switch (nIndex)
+ {
+ case 0:
+ memcpy(lpOutput, &gSysContext,
+ sizeof(LOGCONTEXTA));
+ rc = sizeof(LOGCONTEXTA);
+ break;
+ case CTX_NAME:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcName,
+ strlen(gSysContext.lcName)+1);
+ break;
+ case CTX_OPTIONS:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcOptions,
+ sizeof(UINT));
+ break;
+ case CTX_STATUS:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcStatus,
+ sizeof(UINT));
+ break;
+ case CTX_LOCKS:
+ rc= CopyTabletData (lpOutput, &gSysContext.lcLocks,
+ sizeof(UINT));
+ break;
+ case CTX_MSGBASE:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcMsgBase,
+ sizeof(UINT));
+ break;
+ case CTX_DEVICE:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcDevice,
+ sizeof(UINT));
+ break;
+ case CTX_PKTRATE:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcPktRate,
+ sizeof(UINT));
+ break;
+ case CTX_PKTMODE:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcPktMode,
+ sizeof(WTPKT));
+ break;
+ case CTX_MOVEMASK:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcMoveMask,
+ sizeof(WTPKT));
+ break;
+ case CTX_BTNDNMASK:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcBtnDnMask,
+ sizeof(DWORD));
+ break;
+ case CTX_BTNUPMASK:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcBtnUpMask,
+ sizeof(DWORD));
+ break;
+ case CTX_INORGX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcInOrgX,
+ sizeof(LONG));
+ break;
+ case CTX_INORGY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcInOrgY,
+ sizeof(LONG));
+ break;
+ case CTX_INORGZ:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcInOrgZ,
+ sizeof(LONG));
+ break;
+ case CTX_INEXTX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcInExtX,
+ sizeof(LONG));
+ break;
+ case CTX_INEXTY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcInExtY,
+ sizeof(LONG));
+ break;
+ case CTX_INEXTZ:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcInExtZ,
+ sizeof(LONG));
+ break;
+ case CTX_OUTORGX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcOutOrgX,
+ sizeof(LONG));
+ break;
+ case CTX_OUTORGY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcOutOrgY,
+ sizeof(LONG));
+ break;
+ case CTX_OUTORGZ:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcOutOrgZ,
+ sizeof(LONG));
+ break;
+ case CTX_OUTEXTX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcOutExtX,
+ sizeof(LONG));
+ break;
+ case CTX_OUTEXTY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcOutExtY,
+ sizeof(LONG));
+ break;
+ case CTX_OUTEXTZ:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcOutExtZ,
+ sizeof(LONG));
+ break;
+ case CTX_SENSX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSensX,
+ sizeof(LONG));
+ break;
+ case CTX_SENSY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSensY,
+ sizeof(LONG));
+ break;
+ case CTX_SENSZ:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSensZ,
+ sizeof(LONG));
+ break;
+ case CTX_SYSMODE:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSysMode,
+ sizeof(LONG));
+ break;
+ case CTX_SYSORGX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSysOrgX,
+ sizeof(LONG));
+ break;
+ case CTX_SYSORGY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSysOrgY,
+ sizeof(LONG));
+ break;
+ case CTX_SYSEXTX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSysExtX,
+ sizeof(LONG));
+ break;
+ case CTX_SYSEXTY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSysExtY,
+ sizeof(LONG));
+ break;
+ case CTX_SYSSENSX:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSysSensX,
+ sizeof(LONG));
+ break;
+ case CTX_SYSSENSY:
+ rc = CopyTabletData(lpOutput, &gSysContext.lcSysSensY,
+ sizeof(LONG));
+ break;
+ default:
+ FIXME("WTI_DEFSYSCTX unhandled index %i\n",nIndex);
+ rc = 0;
+ }
+ break;
+ case WTI_CURSORS:
+ case WTI_CURSORS+1:
+ case WTI_CURSORS+2:
+ case WTI_CURSORS+3:
+ case WTI_CURSORS+4:
+ case WTI_CURSORS+5:
+ case WTI_CURSORS+6:
+ case WTI_CURSORS+7:
+ case WTI_CURSORS+8:
+ case WTI_CURSORS+9:
+ case WTI_CURSORS+10:
+ tgtcursor = &gSysCursor[wCategory - WTI_CURSORS];
+ switch (nIndex)
+ {
+ case CSR_NAME:
+ rc = CopyTabletData(lpOutput, &tgtcursor->NAME,
+ strlen(tgtcursor->NAME)+1);
+ break;
+ case CSR_ACTIVE:
+ rc = CopyTabletData(lpOutput,&tgtcursor->ACTIVE,
+ sizeof(BOOL));
+ break;
+ case CSR_PKTDATA:
+ rc = CopyTabletData(lpOutput,&tgtcursor->PKTDATA,
+ sizeof(WTPKT));
+ break;
+ case CSR_BUTTONS:
+ rc = CopyTabletData(lpOutput,&tgtcursor->BUTTONS,
+ sizeof(BYTE));
+ break;
+ case CSR_BUTTONBITS:
+ rc = CopyTabletData(lpOutput,&tgtcursor->BUTTONBITS,
+ sizeof(BYTE));
+ break;
+ case CSR_BTNNAMES:
+ FIXME("Button Names not returned correctly\n");
+ rc = CopyTabletData(lpOutput,&tgtcursor->BTNNAMES,
+ strlen(tgtcursor->BTNNAMES)+1);
+ break;
+ case CSR_BUTTONMAP:
+ rc = CopyTabletData(lpOutput,&tgtcursor->BUTTONMAP,
+ sizeof(BYTE)*32);
+ break;
+ case CSR_SYSBTNMAP:
+ rc = CopyTabletData(lpOutput,&tgtcursor->SYSBTNMAP,
+ sizeof(BYTE)*32);
+ break;
+ case CSR_NPBTNMARKS:
+ memcpy(lpOutput,&tgtcursor->NPBTNMARKS,sizeof(UINT)*2);
+ rc = sizeof(UINT)*2;
+ break;
+ case CSR_NPBUTTON:
+ rc = CopyTabletData(lpOutput,&tgtcursor->NPBUTTON,
+ sizeof(BYTE));
+ break;
+ case CSR_NPRESPONSE:
+ FIXME("Not returning CSR_NPRESPONSE correctly\n");
+ rc = 0;
+ break;
+ case CSR_TPBUTTON:
+ rc = CopyTabletData(lpOutput,&tgtcursor->TPBUTTON,
+ sizeof(BYTE));
+ break;
+ case CSR_TPBTNMARKS:
+ memcpy(lpOutput,&tgtcursor->TPBTNMARKS,sizeof(UINT)*2);
+ rc = sizeof(UINT)*2;
+ break;
+ case CSR_TPRESPONSE:
+ FIXME("Not returning CSR_TPRESPONSE correctly\n");
+ rc = 0;
+ break;
+ case CSR_PHYSID:
+ {
+ DWORD id;
+ rc = CopyTabletData(&id,&tgtcursor->PHYSID,
+ sizeof(DWORD));
+ id += (wCategory - WTI_CURSORS);
+ memcpy(lpOutput,&id,sizeof(DWORD));
+ }
+ break;
+ case CSR_MODE:
+ rc = CopyTabletData(lpOutput,&tgtcursor->MODE,sizeof(UINT));
+ break;
+ case CSR_MINPKTDATA:
+ rc = CopyTabletData(lpOutput,&tgtcursor->MINPKTDATA,
+ sizeof(UINT));
+ break;
+ case CSR_MINBUTTONS:
+ rc = CopyTabletData(lpOutput,&tgtcursor->MINBUTTONS,
+ sizeof(UINT));
+ break;
+ case CSR_CAPABILITIES:
+ rc = CopyTabletData(lpOutput,&tgtcursor->CAPABILITIES,
+ sizeof(UINT));
+ break;
+ case CSR_TYPE:
+ rc = CopyTabletData(lpOutput,&tgtcursor->TYPE,
+ sizeof(UINT));
+ break;
+ default:
+ FIXME("WTI_CURSORS unhandled index %i\n",nIndex);
+ rc = 0;
+ }
+ break;
+ case WTI_DEVICES:
+ switch (nIndex)
+ {
+ case DVC_NAME:
+ rc = CopyTabletData(lpOutput,gSysDevice.NAME,
+ strlen(gSysDevice.NAME)+1);
+ break;
+ case DVC_HARDWARE:
+ rc = CopyTabletData(lpOutput,&gSysDevice.HARDWARE,
+ sizeof(UINT));
+ break;
+ case DVC_NCSRTYPES:
+ rc = CopyTabletData(lpOutput,&gSysDevice.NCSRTYPES,
+ sizeof(UINT));
+ break;
+ case DVC_FIRSTCSR:
+ rc = CopyTabletData(lpOutput,&gSysDevice.FIRSTCSR,
+ sizeof(UINT));
+ break;
+ case DVC_PKTRATE:
+ rc = CopyTabletData(lpOutput,&gSysDevice.PKTRATE,
+ sizeof(UINT));
+ break;
+ case DVC_PKTDATA:
+ rc = CopyTabletData(lpOutput,&gSysDevice.PKTDATA,
+ sizeof(WTPKT));
+ break;
+ case DVC_PKTMODE:
+ rc = CopyTabletData(lpOutput,&gSysDevice.PKTMODE,
+ sizeof(WTPKT));
+ break;
+ case DVC_CSRDATA:
+ rc = CopyTabletData(lpOutput,&gSysDevice.CSRDATA,
+ sizeof(WTPKT));
+ break;
+ case DVC_XMARGIN:
+ rc = CopyTabletData(lpOutput,&gSysDevice.XMARGIN,
+ sizeof(INT));
+ break;
+ case DVC_YMARGIN:
+ rc = CopyTabletData(lpOutput,&gSysDevice.YMARGIN,
+ sizeof(INT));
+ break;
+ case DVC_ZMARGIN:
+ rc = 0; /* unsupported */
+ /*
+ rc = CopyTabletData(lpOutput,&gSysDevice.ZMARGIN,
+ sizeof(INT));
+ */
+ break;
+ case DVC_X:
+ rc = CopyTabletData(lpOutput,&gSysDevice.X,
+ sizeof(AXIS));
+ break;
+ case DVC_Y:
+ rc = CopyTabletData(lpOutput,&gSysDevice.Y,
+ sizeof(AXIS));
+ break;
+ case DVC_Z:
+ rc = 0; /* unsupported */
+ /*
+ rc = CopyTabletData(lpOutput,&gSysDevice.Z,
+ sizeof(AXIS));
+ */
+ break;
+ case DVC_NPRESSURE:
+ rc = CopyTabletData(lpOutput,&gSysDevice.NPRESSURE,
+ sizeof(AXIS));
+ break;
+ case DVC_TPRESSURE:
+ rc = 0; /* unsupported */
+ /*
+ rc = CopyTabletData(lpOutput,&gSysDevice.TPRESSURE,
+ sizeof(AXIS));
+ */
+ break;
+ case DVC_ORIENTATION:
+ memcpy(lpOutput,&gSysDevice.ORIENTATION,sizeof(AXIS)*3);
+ rc = sizeof(AXIS)*3;
+ break;
+ case DVC_ROTATION:
+ rc = 0; /* unsupported */
+ /*
+ memcpy(lpOutput,&gSysDevice.ROTATION,sizeof(AXIS)*3);
+ rc = sizeof(AXIS)*3;
+ */
+ break;
+ case DVC_PNPID:
+ rc = CopyTabletData(lpOutput,gSysDevice.PNPID,
+ strlen(gSysDevice.PNPID)+1);
+ break;
+ default:
+ FIXME("WTI_DEVICES unhandled index %i\n",nIndex);
+ rc = 0;
+ }
+ break;
+ default:
+ FIXME("Unhandled Category %i\n",wCategory);
+ }
+ return rc;
+}
1
0
I am trying to write a simple serial port library
that has the same lowest-common-denominator interface
under both Windows and Linux. I developed it under wine
using mingw32 - i want my apps that use it to work under
both windows and linux (these are command-line apps only).
i noticed that wine does not seem to report errors
from GetOverLapped.. and Wait..Object in a way that
makes sense. By ignoring errors, and just looking at
the dwRead result, i got it to work.
my attempt at a posix-like read function is below
(the linux version follows further down)
best wishes
-paul
---------
int serial_port_read (struct serial_port *p, unsigned char *buf, int
buf_len, int milli_timeout)
{
static int c = 0;
DWORD dwStart, dwRead, r;
int l, total = 0;
dwStart = GetTickCount ();
while (buf_len > 0) {
l = serial_port_chars_avail (p);
if (l < 0)
return total > 0 ? total : SERIAL_PORT_ERROR;
if (l == 0 && GetTickCount () - dwStart >= milli_timeout)
return total > 0 ? total : SERIAL_PORT_TIMEOUT;
/* Like POSIX read(), we return without blocking if we have read at
least one character: */
if (l == 0 && total >= 1)
return total;
/* Read minimum of one character, and a maximum of buf_len
characters: */
CLIP (l, 1, buf_len);
dwRead = 0;
if (!ReadFile (p->hComm, buf, l, &dwRead, &p->osRead)) {
if ((r = GetLastError ()) == ERROR_IO_PENDING) {
dwRead = 0;
GetOverlappedResult (p->hComm, &p->osRead, &dwRead, TRUE);
}
}
total += dwRead;
buf_len -= dwRead;
buf += dwRead;
}
return total;
}
------------
int serial_port_read (struct serial_port *p, unsigned char *buf, int
buf_len, int milli_timeout)
{
int c;
struct timeval tv;
fd_set rd;
if (!buf_len)
return 0;
FD_ZERO (&rd);
FD_SET (p->fd, &rd);
switch (select (p->fd + 1, &rd, NULL, NULL, milli_timeout < 0 ? NULL
: (tv.tv_sec = milli_timeout / 1000,
tv.tv_usec = milli_timeout % 1000,
&tv))) {
case 1:
c = read (p->fd, buf, buf_len);
if (!c) {
p->error = 0;
return SERIAL_PORT_HANGUP;
}
if (c < 0) {
p->error = errno;
return SERIAL_PORT_ERROR;
}
return c;
case 0:
return SERIAL_PORT_TIMEOUT;
default:
p->error = errno;
return SERIAL_PORT_ERROR;
}
}
1
0
Hi
I have posted this problem already long time ago but I never found the
time to test it again.
http://www.winehq.org/hypermail/wine-devel/2002/11/1530.html
But now I tried with the actual wine from cvs (2003-12-03) and it's still
there. The problem is that if I try to create a table in a Access database
the last char of the name gets lost. I now have a small program which
shows this error, maybe someone can use it for finding this bug.
It's a simple VC6 Wizard generated Win32 command line app which
uses MFC for DAO.
The first time I tried was with SUSE 8.0, now I have Knoppix 3.3 (Debian).
Thanks
bye Fabi
---------------------------- mdbtest.cpp
// mdbtest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <afxdao.h> // MFC DAO database classes
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
LPCTSTR lpszTableNodeTable = "Nodes";
LPCTSTR lpszTableNodeName = "NodeName";
LPCTSTR lpszTableNodeNodeType = "NodeType";
LPCTSTR lpszTableNodeBuildCL = "BuildCL";
LPCTSTR lpszTableNodeEditCL = "EditCL";
LPCTSTR lpszTableNodePath = "Path";
LPCTSTR lpszTableNodeFlags = "Flags";
LPCTSTR lpszTableNodeParam1 = "Param1";
LPCTSTR lpszTableNodeParam2 = "Param2";
LPCTSTR lpszTableNodeParam3 = "Param3";
LPCTSTR lpszTableNodeParam4 = "Param4";
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
//-----------------------------------------------------------------------------
void CreateNodeTable(CDaoDatabase* pDB) // throws CDaoException and CMemoryException
{
CDaoTableDef nodeDef(pDB);
// attempt to create a new table def
nodeDef.Create(lpszTableNodeTable);
CDaoFieldInfo DaoFieldInfo;
// init members
DaoFieldInfo.m_strName = lpszTableNodeName;
DaoFieldInfo.m_nType = dbText;
DaoFieldInfo.m_lSize = 255;
DaoFieldInfo.m_lAttributes = dbVariableField;
DaoFieldInfo.m_nOrdinalPosition = 0;
DaoFieldInfo.m_bRequired = FALSE;
DaoFieldInfo.m_bAllowZeroLength = TRUE;
DaoFieldInfo.m_lCollatingOrder = 0;
// create name field
nodeDef.CreateField(DaoFieldInfo);
// create type field
DaoFieldInfo.m_strName = lpszTableNodeNodeType;
DaoFieldInfo.m_lSize = 30;
nodeDef.CreateField(DaoFieldInfo);
// create path field
DaoFieldInfo.m_strName = lpszTableNodePath;
DaoFieldInfo.m_lSize = 255;
nodeDef.CreateField(DaoFieldInfo);
// create flags field
nodeDef.CreateField(lpszTableNodeFlags, dbLong, 4, dbFixedField);
// create build command line field
DaoFieldInfo.m_strName = lpszTableNodeBuildCL;
DaoFieldInfo.m_nType = dbMemo;
nodeDef.CreateField(DaoFieldInfo);
// create edit command line field
DaoFieldInfo.m_strName = lpszTableNodeEditCL;
DaoFieldInfo.m_nType = dbMemo;
nodeDef.CreateField(DaoFieldInfo);
DaoFieldInfo.m_nType = dbMemo;
//DaoFieldInfo.m_lSize = 200;
DaoFieldInfo.m_strName = lpszTableNodeParam1;
nodeDef.CreateField(DaoFieldInfo); // create Param cl field
DaoFieldInfo.m_strName = lpszTableNodeParam2;
nodeDef.CreateField(DaoFieldInfo); // create Param cl field
DaoFieldInfo.m_strName = lpszTableNodeParam3;
nodeDef.CreateField(DaoFieldInfo); // create Param cl field
DaoFieldInfo.m_strName = lpszTableNodeParam4;
nodeDef.CreateField(DaoFieldInfo); // create Param cl field
nodeDef.Append(); // append table def to database
nodeDef.Close();
// end CreateNodeTable
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
CString m_Path="test.mdb";
CDaoDatabase db;
CDaoRecordset rsNode(&db); // rs node
try {
// create database if not available
cout << "Creating Database\n";
try {
db.Create(m_Path);
db.Close();
}
catch (CDaoException* de) {
// already exists I guess
cout << "Database already existed\n";
de->Delete();
}
cout << "Opening Database\n";
// try to open database
db.Open(m_Path, TRUE, FALSE, _T(""));
// --- delete old node and dependency tables and save new ones ---
cout << "Deleting Tabledef\n";
try {
// delete node table
db.DeleteTableDef(lpszTableNodeTable);
} catch (CDaoException* dde) {
// catch error, maybe table def hasn't existed
dde->Delete();
}
cout << "Creating new Tabledef\n";
// create node table
CreateNodeTable(&db);
cout << "Opening Table\n";
// try to open node table
rsNode.Open(dbOpenDynaset, CString(_T("Select * From [")) + lpszTableNodeTable + _T("]"), 0);
// look if recordset is updateable
if (!rsNode.CanUpdate()) {
cout << "No Update\n";
}
cout << "Closing Table\n";
// close tables
rsNode.Close();
cout << "Closing Database\n";
// close database
db.Close();
cout << "Deleting Database\n";
DeleteFile(m_Path);
}
catch (CDaoException* de) { // catch database exception
char msg[256];
de->GetErrorMessage(msg, 256);
cout << "Error:" << msg;
de->Delete();
goto failure_exit;
}
catch (CMemoryException* me) { // catch memory exception
char msg[256];
me->GetErrorMessage(msg, 256);
cout << "Error:" << msg;
me->Delete();
goto failure_exit;
}
cout << "OK\n";
return TRUE;
failure_exit:
// do some cleanup after failure
if (rsNode.IsOpen())
rsNode.Close();
if (db.IsOpen())
db.Close();
}
cout << "ERROR!!\n";
return nRetCode;
}
2
8
Hello wine-devel,
I've got a problem with 'Theme Hospital' game application. It requires
some keyboard input to start.
But the problem is that this application doesn't receive any
keyboard events.
I've put some checks inside function process_events (dlls/x11drv/event.c)
but they show that no keyboard events (KeyPress, KeyRelease)
I don't have any experience in this area and ask for you guidance,
where to look at.
--
Best regards,
Oleg mailto:xolegpro(a)rbcmail.ru
3
3
Robert van Herk <robert(a)robertvanherk.nl> writes:
> This is a patch that adds a tabsheet to winecfg, that allows the user
> to change the dll overrides; both globally and per app.
I put this in, but note that there are more possibilities that just
builtin or native that will have to be supported too. Altogether there
are five possible combinations: "builtin", "native", "builtin,native",
"native,builtin", "".
--
Alexandre Julliard
julliard(a)winehq.com
3
2