qcap: Print GetLastError() in decimal with '%u'.
[wine] / dlls / sane.ds / ds_ctrl.c
1 /*
2  * Copyright 2000 Corel Corporation
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #ifdef HAVE_UNISTD_H
22 # include <unistd.h>
23 #endif
24 #include <stdlib.h>
25 #include "twain.h"
26 #include "sane_i.h"
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(twain);
30
31 /* DG_CONTROL/DAT_CAPABILITY/MSG_GET */
32 TW_UINT16 SANE_CapabilityGet (pTW_IDENTITY pOrigin, TW_MEMREF pData)
33 {
34     TW_UINT16 twRC = TWRC_SUCCESS, twCC = TWCC_SUCCESS;
35     pTW_CAPABILITY pCapability = (pTW_CAPABILITY) pData;
36
37     TRACE("DG_CONTROL/DAT_CAPABILITY/MSG_GET\n");
38
39     if (activeDS.currentState < 4 || activeDS.currentState > 7)
40     {
41         twRC = TWRC_FAILURE;
42         activeDS.twCC = TWCC_SEQERROR;
43     }
44     else
45     {
46         twCC = SANE_SaneCapability (pCapability, MSG_GET);
47         twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE;
48         activeDS.twCC = twCC;
49     }
50
51     return twRC;
52 }
53
54 /* DG_CONTROL/DAT_CAPABILITY/MSG_GETCURRENT */
55 TW_UINT16 SANE_CapabilityGetCurrent (pTW_IDENTITY pOrigin, TW_MEMREF pData)
56 {
57     TW_UINT16 twRC = TWRC_SUCCESS, twCC = TWCC_SUCCESS;
58     pTW_CAPABILITY pCapability = (pTW_CAPABILITY) pData;
59
60     TRACE("DG_CONTROL/DAT_CAPABILITY/MSG_GETCURRENT\n");
61
62     if (activeDS.currentState < 4 || activeDS.currentState > 7)
63     {
64         twRC = TWRC_FAILURE;
65         activeDS.twCC = TWCC_SEQERROR;
66     }
67     else
68     {
69         twCC = SANE_SaneCapability (pCapability, MSG_GETCURRENT);
70         twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE;
71         activeDS.twCC = twCC;
72     }
73
74     return twRC;
75 }
76
77 /* DG_CONTROL/DAT_CAPABILITY/MSG_GETDEFAULT */
78 TW_UINT16 SANE_CapabilityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
79 {
80     TW_UINT16 twRC = TWRC_SUCCESS, twCC = TWCC_SUCCESS;
81     pTW_CAPABILITY pCapability = (pTW_CAPABILITY) pData;
82
83     TRACE("DG_CONTROL/DAT_CAPABILITY/MSG_GETDEFAULT\n");
84
85     if (activeDS.currentState < 4 || activeDS.currentState > 7)
86     {
87         twRC = TWRC_FAILURE;
88         activeDS.twCC = TWCC_SEQERROR;
89     }
90     else
91     {
92         twCC = SANE_SaneCapability (pCapability, MSG_GETDEFAULT);
93         twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE;
94         activeDS.twCC = twCC;
95     }
96
97     return twRC;
98 }
99
100 /* DG_CONTROL/DAT_CAPABILITY/MSG_QUERYSUPPORT */
101 TW_UINT16 SANE_CapabilityQuerySupport (pTW_IDENTITY pOrigin,
102                                         TW_MEMREF pData)
103 {
104     FIXME ("stub!\n");
105
106     return TWRC_FAILURE;
107 }
108
109 /* DG_CONTROL/DAT_CAPABILITY/MSG_RESET */
110 TW_UINT16 SANE_CapabilityReset (pTW_IDENTITY pOrigin, 
111                                  TW_MEMREF pData)
112 {
113     TW_UINT16 twRC = TWRC_SUCCESS, twCC = TWCC_SUCCESS;
114     pTW_CAPABILITY pCapability = (pTW_CAPABILITY) pData;
115
116     TRACE("DG_CONTROL/DAT_CAPABILITY/MSG_RESET\n");
117
118     if (activeDS.currentState < 4 || activeDS.currentState > 7)
119     {
120         twRC = TWRC_FAILURE;
121         activeDS.twCC = TWCC_SEQERROR;
122     }
123     else
124     {
125         twCC = SANE_SaneCapability (pCapability, MSG_RESET);
126         twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE;
127         activeDS.twCC = twCC;
128     }
129
130     return twRC;
131 }
132
133 /* DG_CONTROL/DAT_CAPABILITY/MSG_SET */
134 TW_UINT16 SANE_CapabilitySet (pTW_IDENTITY pOrigin, 
135                                TW_MEMREF pData)
136 {
137     TW_UINT16 twRC = TWRC_SUCCESS, twCC = TWCC_SUCCESS;
138     pTW_CAPABILITY pCapability = (pTW_CAPABILITY) pData;
139
140     TRACE ("DG_CONTROL/DAT_CAPABILITY/MSG_SET\n");
141
142     if (activeDS.currentState != 4)
143     {
144         twRC = TWRC_FAILURE;
145         activeDS.twCC = TWCC_SEQERROR;
146     }
147     else
148     {
149         twCC = SANE_SaneCapability (pCapability, MSG_SET);
150         twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE;
151         activeDS.twCC = twCC;
152     }
153     return twRC;
154 }
155
156 /* DG_CONTROL/DAT_CUSTOMDSDATA/MSG_GET */
157 TW_UINT16 SANE_CustomDSDataGet (pTW_IDENTITY pOrigin, 
158                                 TW_MEMREF pData)
159 {
160     FIXME ("stub!\n");
161
162     return TWRC_FAILURE;
163 }
164
165 /* DG_CONTROL/DAT_CUSTOMDSDATA/MSG_SET */
166 TW_UINT16 SANE_CustomDSDataSet (pTW_IDENTITY pOrigin, 
167                                  TW_MEMREF pData)
168 {
169     FIXME ("stub!\n");
170
171     return TWRC_FAILURE;
172 }
173
174 /* DG_CONTROL/DAT_FILESYSTEM/MSG_AUTOMATICCAPTUREDIRECTORY */
175 TW_UINT16 SANE_AutomaticCaptureDirectory (pTW_IDENTITY pOrigin,
176                                            
177                                            TW_MEMREF pData)
178 {
179     FIXME ("stub!\n");
180
181     return TWRC_FAILURE;
182 }
183
184 /* DG_CONTROL/DAT_FILESYSTEM/MSG_CHANGEDIRECTORY */
185 TW_UINT16 SANE_ChangeDirectory (pTW_IDENTITY pOrigin, 
186                                  TW_MEMREF pData)
187 {
188     FIXME ("stub!\n");
189
190     return TWRC_FAILURE;
191 }
192
193 /* DG_CONTROL/DAT_FILESYSTEM/MSG_COPY */
194 TW_UINT16 SANE_FileSystemCopy (pTW_IDENTITY pOrigin, 
195                                 TW_MEMREF pData)
196 {
197     FIXME ("stub!\n");
198
199     return TWRC_FAILURE;
200 }
201
202 /* DG_CONTROL/DAT_FILESYSTEM/MSG_CREATEDIRECTORY */
203 TW_UINT16 SANE_CreateDirectory (pTW_IDENTITY pOrigin, 
204                                  TW_MEMREF pData)
205 {
206     FIXME ("stub!\n");
207
208     return TWRC_FAILURE;
209 }
210
211 /* DG_CONTROL/DAT_FILESYSTEM/MSG_DELETE */
212 TW_UINT16 SANE_FileSystemDelete (pTW_IDENTITY pOrigin, 
213                                   TW_MEMREF pData)
214 {
215     FIXME ("stub!\n");
216
217     return TWRC_FAILURE;
218 }
219
220 /* DG_CONTROL/DAT_FILESYSTEM/MSG_FORMATMEDIA */
221 TW_UINT16 SANE_FormatMedia (pTW_IDENTITY pOrigin, 
222                              TW_MEMREF pData)
223 {
224     FIXME ("stub!\n");
225
226     return TWRC_FAILURE;
227 }
228
229 /* DG_CONTROL/DAT_FILESYSTEM/MSG_GETCLOSE */
230 TW_UINT16 SANE_FileSystemGetClose (pTW_IDENTITY pOrigin, 
231                                     TW_MEMREF pData)
232 {
233     FIXME ("stub!\n");
234
235     return TWRC_FAILURE;
236 }
237
238 /* DG_CONTROL/DAT_FILESYSTEM/MSG_GETFIRSTFILE */
239 TW_UINT16 SANE_FileSystemGetFirstFile (pTW_IDENTITY pOrigin,
240                                         
241                                         TW_MEMREF pData)
242 {
243     FIXME ("stub!\n");
244
245     return TWRC_FAILURE;
246 }
247
248 /* DG_CONTROL/DAT_FILESYSTEM/MSG_GETINFO */
249 TW_UINT16 SANE_FileSystemGetInfo (pTW_IDENTITY pOrigin, 
250                                    TW_MEMREF pData)
251 {
252     FIXME ("stub!\n");
253
254     return TWRC_FAILURE;
255 }
256
257 /* DG_CONTROL/DAT_FILESYSTEM/MSG_GETNEXTFILE */
258 TW_UINT16 SANE_FileSystemGetNextFile (pTW_IDENTITY pOrigin,
259                                        
260                                        TW_MEMREF pData)
261 {
262     FIXME ("stub!\n");
263
264     return TWRC_FAILURE;
265 }
266
267 /* DG_CONTROL/DAT_FILESYSTEM/MSG_RENAME */
268 TW_UINT16 SANE_FileSystemRename (pTW_IDENTITY pOrigin, 
269                                   TW_MEMREF pData)
270 {
271     FIXME ("stub!\n");
272
273     return TWRC_FAILURE;
274 }
275
276 /* DG_CONTROL/DAT_EVENT/MSG_PROCESSEVENT */
277 TW_UINT16 SANE_ProcessEvent (pTW_IDENTITY pOrigin, 
278                               TW_MEMREF pData)
279 {
280     TW_UINT16 twRC = TWRC_SUCCESS;
281     pTW_EVENT pEvent = (pTW_EVENT) pData;
282
283     TRACE("DG_CONTROL/DAT_EVENT/MSG_PROCESSEVENT\n");
284
285     if (activeDS.currentState < 5 || activeDS.currentState > 7)
286     {
287         twRC = TWRC_FAILURE;
288         activeDS.twCC = TWCC_SEQERROR;
289     }
290     else
291     {
292         if (activeDS.pendingEvent.TWMessage != MSG_NULL)
293         {
294             pEvent->TWMessage = activeDS.pendingEvent.TWMessage;
295             activeDS.pendingEvent.TWMessage = MSG_NULL;
296             twRC = TWRC_NOTDSEVENT;
297         }
298         else
299         {
300             pEvent->TWMessage = MSG_NULL;  /* no message to the application */
301             twRC = TWRC_NOTDSEVENT;
302         }
303         activeDS.twCC = TWCC_SUCCESS;
304     }
305
306     return twRC;
307 }
308
309 /* DG_CONTROL/DAT_PASSTHRU/MSG_PASSTHRU */
310 TW_UINT16 SANE_PassThrough (pTW_IDENTITY pOrigin, 
311                              TW_MEMREF pData)
312 {
313     FIXME ("stub!\n");
314
315     return TWRC_FAILURE;
316 }
317
318 /* DG_CONTROL/DAT_PENDINGXFERS/MSG_ENDXFER */
319 TW_UINT16 SANE_PendingXfersEndXfer (pTW_IDENTITY pOrigin, 
320                                      TW_MEMREF pData)
321 {
322     TW_UINT16 twRC = TWRC_SUCCESS;
323     pTW_PENDINGXFERS pPendingXfers = (pTW_PENDINGXFERS) pData;
324
325     TRACE("DG_CONTROL/DAT_PENDINGXFERS/MSG_ENDXFER\n");
326
327     if (activeDS.currentState != 6 && activeDS.currentState != 7)
328     {
329         twRC = TWRC_FAILURE;
330         activeDS.twCC = TWCC_SEQERROR;
331     }
332     else
333     {
334         if (pPendingXfers->Count != 0)
335         {
336             pPendingXfers->Count --;
337             activeDS.currentState = 6;
338         }
339         else
340         {
341             activeDS.currentState = 5;
342             /* Notify the application that it can close the data source */
343             activeDS.pendingEvent.TWMessage = MSG_CLOSEDSREQ;
344         }
345         twRC = TWRC_SUCCESS;
346         activeDS.twCC = TWCC_SUCCESS;
347     }
348
349     return twRC;
350 }
351
352 /* DG_CONTROL/DAT_PENDINGXFERS/MSG_GET */
353 TW_UINT16 SANE_PendingXfersGet (pTW_IDENTITY pOrigin, 
354                                  TW_MEMREF pData)
355 {
356     TW_UINT16 twRC = TWRC_SUCCESS;
357     pTW_PENDINGXFERS pPendingXfers = (pTW_PENDINGXFERS) pData;
358
359     TRACE("DG_CONTROL/DAT_PENDINGXFERS/MSG_GET\n");
360
361     if (activeDS.currentState < 4 || activeDS.currentState > 7)
362     {
363         twRC = TWRC_FAILURE;
364         activeDS.twCC = TWCC_SEQERROR;
365     }
366     else
367     {
368         /* FIXME: we shouldn't return 1 here */
369         pPendingXfers->Count = 1;
370         twRC = TWRC_SUCCESS;
371         activeDS.twCC = TWCC_SUCCESS;
372     }
373
374     return twRC;
375 }
376
377 /* DG_CONTROL/DAT_PENDINGXFERS/MSG_RESET */
378 TW_UINT16 SANE_PendingXfersReset (pTW_IDENTITY pOrigin, 
379                                    TW_MEMREF pData)
380 {
381     TW_UINT16 twRC = TWRC_SUCCESS;
382     pTW_PENDINGXFERS pPendingXfers = (pTW_PENDINGXFERS) pData;
383
384     TRACE("DG_CONTROL/DAT_PENDINGXFERS/MSG_RESET\n");
385
386     if (activeDS.currentState != 6)
387     {
388         twRC = TWRC_FAILURE;
389         activeDS.twCC = TWCC_SEQERROR;
390     }
391     else
392     {
393         pPendingXfers->Count = 0;
394         activeDS.currentState = 5;
395         twRC = TWRC_SUCCESS;
396         activeDS.twCC = TWCC_SUCCESS;
397     }
398
399     return twRC;
400 }
401
402 /* DG_CONTROL/DAT_PENDINGXFERS/MSG_STOPFEEDER */
403 TW_UINT16 SANE_PendingXfersStopFeeder (pTW_IDENTITY pOrigin,
404                                         TW_MEMREF pData)
405 {
406     FIXME ("stub!\n");
407
408     return TWRC_FAILURE;
409 }
410
411 /* DG_CONTROL/DAT_SETUPFILEXFER/MSG_GET */
412 TW_UINT16 SANE_SetupFileXferGet (pTW_IDENTITY pOrigin, 
413                                   TW_MEMREF pData)
414 {
415     FIXME ("stub!\n");
416
417     return TWRC_FAILURE;
418 }
419
420 /* DG_CONTROL/DAT_SETUPXFER/MSG_GETDEFAULT */
421 TW_UINT16 SANE_SetupFileXferGetDefault (pTW_IDENTITY pOrigin, 
422                                          TW_MEMREF pData)
423 {
424     FIXME ("stub!\n");
425
426     return TWRC_FAILURE;
427 }
428
429
430 /* DG_CONTROL/DAT_SETUPFILEXFER/MSG_RESET */
431 TW_UINT16 SANE_SetupFileXferReset (pTW_IDENTITY pOrigin, 
432                                     TW_MEMREF pData)
433 {
434     FIXME ("stub!\n");
435
436     return TWRC_FAILURE;
437 }
438
439 /* DG_CONTROL/DAT_SETUPFILEXFER/MSG_SET */
440 TW_UINT16 SANE_SetupFileXferSet (pTW_IDENTITY pOrigin, 
441                                   TW_MEMREF pData)
442 {
443     FIXME ("stub!\n");
444
445     return TWRC_FAILURE;
446 }
447
448 /* DG_CONTROL/DAT_SETUPFILEXFER2/MSG_GET */
449 TW_UINT16 SANE_SetupFileXfer2Get (pTW_IDENTITY pOrigin, 
450                                    TW_MEMREF pData)
451 {
452     FIXME ("stub!\n");
453
454     return TWRC_FAILURE;
455 }
456
457 /* DG_CONTROL/DAT_SETUPFILEXFER2/MSG_GETDEFAULT */
458 TW_UINT16 SANE_SetupFileXfer2GetDefault (pTW_IDENTITY pOrigin, 
459                                          TW_MEMREF pData)
460 {
461     FIXME ("stub!\n");
462
463     return TWRC_FAILURE;
464 }
465
466 /* DG_CONTROL/DAT_SETUPFILEXFER2/MSG_RESET */
467 TW_UINT16 SANE_SetupFileXfer2Reset (pTW_IDENTITY pOrigin, 
468                                   TW_MEMREF pData)
469 {
470     FIXME ("stub!\n");
471
472     return TWRC_FAILURE;
473 }
474
475 /* DG_CONTROL/DAT_SETUPFILEXFER2/MSG_SET */
476 TW_UINT16 SANE_SetupFileXfer2Set (pTW_IDENTITY pOrigin, 
477                                   TW_MEMREF pData)
478 {
479     FIXME ("stub!\n");
480
481     return TWRC_FAILURE;
482 }
483
484 /* DG_CONTROL/DAT_SETUPMEMXFER/MSG_GET */
485 TW_UINT16 SANE_SetupMemXferGet (pTW_IDENTITY pOrigin, 
486                                   TW_MEMREF pData)
487 {
488 #ifndef HAVE_SANE
489     return TWRC_FAILURE;
490 #else
491     pTW_SETUPMEMXFER  pSetupMemXfer = (pTW_SETUPMEMXFER)pData;
492
493     TRACE("DG_CONTROL/DAT_SETUPMEMXFER/MSG_GET\n");
494     if (activeDS.sane_param_valid)
495     {
496         pSetupMemXfer->MinBufSize = activeDS.sane_param.bytes_per_line;
497         pSetupMemXfer->MaxBufSize = activeDS.sane_param.bytes_per_line * 8;
498         pSetupMemXfer->Preferred = activeDS.sane_param.bytes_per_line * 2;
499     }
500     else
501     {
502         /* Guessing */
503         pSetupMemXfer->MinBufSize = 2000;
504         pSetupMemXfer->MaxBufSize = 8000;
505         pSetupMemXfer->Preferred = 4000;
506     }
507
508     return TWRC_SUCCESS;
509 #endif
510 }
511
512 /* DG_CONTROL/DAT_STATUS/MSG_GET */
513 TW_UINT16 SANE_GetDSStatus (pTW_IDENTITY pOrigin, 
514                              TW_MEMREF pData)
515 {
516     pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
517
518     TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
519     pSourceStatus->ConditionCode = activeDS.twCC;
520     /* Reset the condition code */
521     activeDS.twCC = TWCC_SUCCESS;
522     return TWRC_SUCCESS;
523 }
524
525 /* DG_CONTROL/DAT_USERINTERFACE/MSG_DISABLEDS */
526 TW_UINT16 SANE_DisableDSUserInterface (pTW_IDENTITY pOrigin,
527                                         TW_MEMREF pData)
528 {
529     TW_UINT16 twRC = TWRC_SUCCESS;
530
531     TRACE ("DG_CONTROL/DAT_USERINTERFACE/MSG_DISABLEDS\n");
532
533     if (activeDS.currentState != 5)
534     {
535         twRC = TWRC_FAILURE;
536         activeDS.twCC = TWCC_SEQERROR;
537     }
538     else
539     {
540         activeDS.currentState = 4;
541         twRC = TWRC_SUCCESS;
542         activeDS.twCC = TWCC_SUCCESS;
543     }
544
545     return twRC;
546 }
547
548 /* DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDS */
549 TW_UINT16 SANE_EnableDSUserInterface (pTW_IDENTITY pOrigin,
550                                        TW_MEMREF pData)
551 {
552     TW_UINT16 twRC = TWRC_SUCCESS;
553     pTW_USERINTERFACE pUserInterface = (pTW_USERINTERFACE) pData;
554
555     TRACE ("DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDS\n");
556
557     if (activeDS.currentState != 4)
558     {
559         twRC = TWRC_FAILURE;
560         activeDS.twCC = TWCC_SEQERROR;
561         FIXME("sequence error %d\n", activeDS.currentState);
562     }
563     else
564     {
565         activeDS.hwndOwner = pUserInterface->hParent;
566         if (pUserInterface->ShowUI)
567         {
568             BOOL rc;
569             activeDS.currentState = 5; /* Transitions to state 5 */
570                 FIXME("showing UI\n");
571             rc = DoScannerUI();
572             if (!rc)
573             {
574                 activeDS.pendingEvent.TWMessage = MSG_CLOSEDSREQ;
575             }
576 #ifdef HAVE_SANE
577             else
578             {
579                 sane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
580                 activeDS.sane_param_valid = TRUE;
581             }
582 #endif
583         }
584         else
585         {
586             /* no UI will be displayed, so source is ready to transfer data */
587             activeDS.pendingEvent.TWMessage = MSG_XFERREADY;
588             activeDS.currentState = 6; /* Transitions to state 6 directly */
589         }
590
591         activeDS.hwndOwner = pUserInterface->hParent;
592         twRC = TWRC_SUCCESS;
593         activeDS.twCC = TWCC_SUCCESS;
594     }
595
596     return twRC;
597 }
598
599 /* DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDSUIONLY */
600 TW_UINT16 SANE_EnableDSUIOnly (pTW_IDENTITY pOrigin, 
601                                 TW_MEMREF pData)
602 {
603     TW_UINT16 twRC = TWRC_SUCCESS;
604
605     TRACE("DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDSUIONLY\n");
606
607     if (activeDS.currentState != 4)
608     {
609         twRC = TWRC_FAILURE;
610         activeDS.twCC = TWCC_SEQERROR;
611     }
612     else
613     {
614         /* FIXME: we should replace xscanimage with our own UI */
615         system ("xscanimage");
616         activeDS.currentState = 5;
617         twRC = TWRC_SUCCESS;
618         activeDS.twCC = TWCC_SUCCESS;
619     }
620
621     return twRC;
622 }
623
624 /* DG_CONTROL/DAT_XFERGROUP/MSG_GET */
625 TW_UINT16 SANE_XferGroupGet (pTW_IDENTITY pOrigin, 
626                               TW_MEMREF pData)
627 {
628     FIXME ("stub!\n");
629
630     return TWRC_FAILURE;
631 }
632
633 /* DG_CONTROL/DAT_XFERGROUP/MSG_SET */
634 TW_UINT16 SANE_XferGroupSet (pTW_IDENTITY pOrigin, 
635                                   TW_MEMREF pData)
636 {
637     FIXME ("stub!\n");
638
639     return TWRC_FAILURE;
640 }